automatische Installation von Updates vervollständigt

master
Niko Diamadis 5 years ago
parent f24fd24c9e
commit a63a75364b

@ -72,4 +72,5 @@ dependencies {
implementation "com.mikepenz:aboutlibraries-core:$about_libraries_version"
implementation 'me.ibrahimyilmaz:kiel:1.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(':installer')
}

@ -5,7 +5,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<application
android:allowBackup="true"
@ -13,6 +12,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/DarkActionbar"
tools:ignore="AllowBackup">
<activity android:name="com.cyb3rko.techniklogger.MainActivity"

@ -1,18 +1,15 @@
package com.cyb3rko.techniklogger
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Base64
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.getInputField
@ -27,7 +24,6 @@ import com.google.firebase.database.*
import es.dmoral.toasty.Toasty
import kotlinx.android.synthetic.main.activity_main.*
import me.ibrahimyilmaz.kiel.adapter.RecyclerViewAdapter.Companion.adapterOf
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.*
@ -232,30 +228,7 @@ class MainActivity : AppCompatActivity() {
.build()
.getAsString(object : StringRequestListener {
override fun onResponse(response: String?) {
var parts = response!!.split("content\":\"".toRegex()).toTypedArray()
var parts2 = parts[1].split("\",\"target".toRegex()).toTypedArray()
val content = String(Base64.decode(parts2[0], Base64.DEFAULT), StandardCharsets.UTF_8)
parts = content.split("versionCode ".toRegex()).toTypedArray()
parts2 = parts[1].split("\n".toRegex()).toTypedArray()
val neuesterVersionCode = parts2[0].toInt()
parts = parts2[1].split("\"".toRegex()).toTypedArray()
parts2 = parts[1].split("\"".toRegex()).toTypedArray()
sharedPrefEditor.putString("neuesteVersion", parts2[0]).apply()
if (BuildConfig.VERSION_CODE != neuesterVersionCode) {
Log.d(this@MainActivity.toString(), "Update verfügbar: ${sharedPref.getString("neuesteVersion", "")}")
updateCheck(this@MainActivity)
ActivityCompat.requestPermissions(
this@MainActivity, arrayOf(
Manifest.permission.INTERNET, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.REQUEST_INSTALL_PACKAGES
), 1
)
} else {
Log.d(this@MainActivity.toString(), "App auf dem neuesten Stand")
}
updateCheck(this@MainActivity)
}
override fun onError(anError: ANError?) {

@ -1,27 +1,18 @@
package com.cyb3rko.techniklogger
import android.Manifest
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.util.Base64
import android.util.Log
import android.webkit.URLUtil
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.afollestad.materialdialogs.MaterialDialog
import com.androidnetworking.AndroidNetworking
import com.androidnetworking.error.ANError
import com.androidnetworking.interfaces.StringRequestListener
import com.cyb3rko.installer.DownloadApk
import es.dmoral.toasty.Toasty
import java.io.File
import java.nio.charset.StandardCharsets
internal fun updateCheck(activity: MainActivity) {
@ -32,15 +23,15 @@ internal fun updateCheck(activity: MainActivity) {
.build()
.getAsString(object : StringRequestListener {
override fun onResponse(response: String?) {
var parts = response!!.split("content\":\"".toRegex()).toTypedArray()
var parts2 = parts[1].split("\",\"target".toRegex()).toTypedArray()
var parts = response!!.split("content\":\"")
var parts2 = parts[1].split("\",\"target")
val content = String(Base64.decode(parts2[0], Base64.DEFAULT), StandardCharsets.UTF_8)
parts = content.split("versionCode ".toRegex()).toTypedArray()
parts2 = parts[1].split("\n".toRegex()).toTypedArray()
parts = content.split("versionCode ")
parts2 = parts[1].split("\n")
val neuesterVersionCode = parts2[0].toInt()
parts = parts2[1].split("\"".toRegex()).toTypedArray()
parts2 = parts[1].split("\"".toRegex()).toTypedArray()
parts = parts2[1].split("\"")
parts2 = parts[1].split("\"")
val newestVersion = parts2[0]
if (BuildConfig.VERSION_CODE != neuesterVersionCode) {
@ -48,10 +39,7 @@ internal fun updateCheck(activity: MainActivity) {
showDownloadDialog(activity, newestVersion)
ActivityCompat.requestPermissions(
activity, arrayOf(
Manifest.permission.INTERNET, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.REQUEST_INSTALL_PACKAGES
), 1
activity, arrayOf(Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1
)
} else {
Log.d(activity.toString(), "App auf dem neuesten Stand")
@ -69,6 +57,7 @@ internal fun showDownloadDialog(context: Context, newestVersion: String) {
title(0, "Neues Update verfügbar")
message(0, "Das Update '$newestVersion' steht zum Download bereit!\n\nAktuell installierte Version: '${BuildConfig.VERSION_NAME}'")
positiveButton(0, "Herunterladen") {
it.cancel()
downloadApk(context, newestVersion)
}
}
@ -77,44 +66,8 @@ internal fun showDownloadDialog(context: Context, newestVersion: String) {
private fun downloadApk(context: Context, newestVersion: String) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
val link = "https://cdn.cyb3rko.de/Apps/Technik-Logger/Technik-Logger%20v$newestVersion.apk"
println("$newestVersion: $link")
val request = DownloadManager.Request(Uri.parse(link))
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(link, null, null))
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val onCompleteReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val apkPath = "${Environment.getExternalStorageDirectory()}/download/Technik-Logger v$newestVersion.apk"
val file = File(apkPath)
installBelowAndroid10(context!!, file, this)
}
}
context.registerReceiver(onCompleteReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
DownloadApk(context).startDownloadingApk(link)
} else {
Toasty.error(context, "Fehlende Berechtigung, erteilen Sie diese beim nächsten App-Start", Toasty.LENGTH_LONG).show()
}
}
private fun uriFromFile(context: Context, file: File): Uri {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
} else {
Uri.fromFile(file)
}
}
private fun installBelowAndroid10(context: Context, file: File, broadcastReceiver: BroadcastReceiver) {
val installIntent = Intent(Intent.ACTION_VIEW)
installIntent.setDataAndType(uriFromFile(context, file), "application/vnd.android.package-archive")
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(installIntent)
context.unregisterReceiver(broadcastReceiver)
}

@ -0,0 +1 @@
/build

@ -0,0 +1,44 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
minSdkVersion 19
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cyb3rko.installer">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
</manifest>

@ -0,0 +1,150 @@
package com.cyb3rko.installer;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloadApk extends Activity{
private static ProgressDialog bar;
private static Context context;
private static String downloadUrl;
private static String fileName;
public DownloadApk(Context context){
DownloadApk.context = context;
}
public void startDownloadingApk(String url){
downloadUrl = url;
System.out.println(downloadUrl);
fileName = url.split("Technik-Logger/")[1].replace("%20", " ");
System.out.println(fileName);
if (downloadUrl!=null){
new DownloadNewVersion().execute();
}
}
private static class DownloadNewVersion extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
if(bar==null){
bar = new ProgressDialog(context);
bar.setCancelable(false);
bar.setMessage("Downloading...");
bar.setIndeterminate(true);
bar.setCanceledOnTouchOutside(false);
bar.show();
}
}
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
bar.setIndeterminate(false);
bar.setMax(100);
bar.setProgress(progress[0]);
String msg = "";
if (progress[0]>99) {
msg="Finishing... ";
} else {
msg="Downloading... "+progress[0]+"%";
}
bar.setMessage(msg);
}
@Override
protected void onPostExecute(Boolean result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if (bar.isShowing() && bar!=null) {
bar.dismiss();
bar=null;
}
if (result){
Toast.makeText(context,"Update Done", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context,"Error: Try Again", Toast.LENGTH_SHORT).show();
}
}
@Override
protected Boolean doInBackground(String... arg0) {
Boolean flag = false;
try {
URL url = new URL(downloadUrl);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.connect();
String PATH = Environment.getExternalStorageDirectory()+"/Download/";
File file = new File(PATH);
File outputFile = new File(file, fileName);
if(outputFile.exists()){
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
// size of apk
int total_size = c.getContentLength();
byte[] buffer = new byte[1024];
int len1;
int per;
int downloaded=0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
downloaded += len1;
per = downloaded * 100 / total_size;
publishProgress(per);
}
fos.close();
is.close();
openNewVersion(PATH);
flag = true;
} catch (MalformedURLException e) {
String TAG = "DownloadApk";
Log.e(TAG, "Update Error: " + e.getMessage());
flag = false;
} catch (IOException ex) {
ex.printStackTrace();
}
return flag;
}
}
private static void openNewVersion(String location) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(getUriFromFile(location), "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);
}
private static Uri getUriFromFile(String location) {
if (Build.VERSION.SDK_INT<24) {
return Uri.fromFile(new File(location + fileName));
} else {
return FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider",
new File(location + fileName));
}
}
}

@ -1,2 +1,3 @@
include ':installer'
include ':app'
rootProject.name = "Technik-Logger"
Loading…
Cancel
Save