Aktualisiere Installer und optimiere Updater-Code

master
Niko Diamadis 4 years ago
parent 2ebc7b4c97
commit 74b53d7630
Signed by: niko
GPG Key ID: BE53B0B17B1B142E

@ -74,5 +74,4 @@ dependencies {
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.google.firebase:firebase-crashlytics-ktx' // BOM versioning
implementation 'com.itextpdf:itextpdf:5.5.13.2'
implementation project(':installer')
}

@ -5,6 +5,7 @@
<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"

@ -15,6 +15,7 @@ import com.androidnetworking.AndroidNetworking
import com.androidnetworking.error.ANError
import com.androidnetworking.interfaces.StringRequestListener
import com.cyb3rko.techniklogger.databinding.ActivityMainBinding
import com.cyb3rko.techniklogger.update.Updater
class MainActivity : AppCompatActivity() {
private var _binding: ActivityMainBinding? = null
@ -49,7 +50,7 @@ class MainActivity : AppCompatActivity() {
if (sharedPref.getString("name", "") == "") {
showNameDialog(this, sharedPref, sharedPrefEditor, false)
} else {
updateCheck()
callUpdateCheck()
}
}
@ -60,7 +61,7 @@ class MainActivity : AppCompatActivity() {
)
}
private fun updateCheck() {
private fun callUpdateCheck() {
AndroidNetworking.initialize(applicationContext)
AndroidNetworking.get("https://git.aldiserver.de/api/v1/repos/niko/technik-logger-app/contents/app/build.gradle")
.addHeaders("Authorization", "token d70a26aa455b25e60885ba5ff31cce231d454f82")
@ -68,7 +69,7 @@ class MainActivity : AppCompatActivity() {
.build()
.getAsString(object : StringRequestListener {
override fun onResponse(response: String?) {
updateCheck(this@MainActivity)
Updater.updateCheck(this@MainActivity)
}
override fun onError(anError: ANError?) {

@ -1,77 +0,0 @@
package com.cyb3rko.techniklogger
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
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 com.google.android.material.dialog.MaterialAlertDialogBuilder
import es.dmoral.toasty.Toasty
import java.nio.charset.StandardCharsets
internal fun updateCheck(activity: MainActivity) {
AndroidNetworking.initialize(activity)
AndroidNetworking.get("https://git.aldiserver.de/api/v1/repos/niko/technik-logger-app/contents/app/build.gradle")
.addHeaders("Authorization", "token d70a26aa455b25e60885ba5ff31cce231d454f82")
.doNotCacheResponse()
.build()
.getAsString(object : StringRequestListener {
override fun onResponse(response: String?) {
try {
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 ")
parts2 = parts[1].split("\n")
val neuesterVersionCode = parts2[0].toInt()
parts = parts2[1].split("\"")
parts2 = parts[1].split("\"")
val newestVersion = parts2[0]
if (BuildConfig.VERSION_CODE != neuesterVersionCode) {
Log.d("Technik-Logger Updater", "Update verfügbar: $newestVersion")
showDownloadDialog(activity, newestVersion)
ActivityCompat.requestPermissions(
activity, arrayOf(Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1
)
} else {
Log.d(activity.toString(), "App auf dem neuesten Stand")
}
} catch (e: Exception) {
Toasty.error(activity, "Update-Check fehlgeschlagen", Toasty.LENGTH_LONG).show()
}
}
override fun onError(anError: ANError?) {
Log.d(activity.toString(), "Update-Abfrage fehlgeschlagen: ${anError!!.errorBody.trimIndent()}")
}
})
}
internal fun showDownloadDialog(context: Context, newestVersion: String) {
MaterialAlertDialogBuilder(context)
.setTitle("Neues Update verfügbar")
.setMessage("Das Update '$newestVersion' steht zum Download bereit!\n\nAktuell installierte Version: '${BuildConfig.VERSION_NAME}'")
.setPositiveButton("Herunterladen") { _, _ ->
downloadApk(context, newestVersion)
}
.show()
}
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"
DownloadApk(context).startDownloadingApk(link)
} else {
Toasty.error(context, "Fehlende Berechtigung, erteile diese beim nächsten App-Start", Toasty.LENGTH_LONG).show()
}
}

@ -0,0 +1,152 @@
package com.cyb3rko.techniklogger.update
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.webkit.URLUtil
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.URL
/**
* Created by piashsarker on 1/16/18, rewritten by Cyb3rKo on 12/21/20.
*/
class DownloadApk(var context: Context) : AppCompatActivity() {
@JvmOverloads
fun startDownloadingApk(url: String, fileName: String = "App Update") {
if (URLUtil.isValidUrl(url)) {
DownloadNewVersion(context, url, fileName).execute()
}
}
@Suppress("DEPRECATION")
private class DownloadNewVersion(
val context: Context,
val downloadUrl: String,
val fileName: String
): AsyncTask<String, Int, Boolean>() {
private lateinit var bar: ProgressDialog
override fun onPreExecute() {
super.onPreExecute()
bar = ProgressDialog(context).apply {
setCancelable(false)
setMessage("Downloading...")
isIndeterminate = true
setCanceledOnTouchOutside(false)
show()
}
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
var msg = ""
val progress = values[0]
if (progress != null) {
bar.progress = progress
msg = if (progress > 99) "Finishing... " else "Downloading... $progress%"
}
bar.apply {
isIndeterminate = false
max = 100
setMessage(msg)
}
}
override fun onPostExecute(result: Boolean?) {
super.onPostExecute(result)
bar.dismiss()
if (result != null && result) {
Toast.makeText(context, "Update Done", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Error: Try Again", Toast.LENGTH_SHORT).show()
}
}
override fun doInBackground(vararg p0: String?): Boolean {
var flag = false
try {
val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/"
var outputFile = File("$path$fileName.apk")
var repetition = 1
while (outputFile.exists()) {
outputFile = File("$path$fileName ($repetition).apk")
repetition++
}
val directory = File(path)
if (!directory.exists()) {
directory.mkdirs()
}
val url = URL(downloadUrl)
val c = url.openConnection() as HttpURLConnection
c.requestMethod = "GET"
c.connect()
val fos = FileOutputStream(outputFile)
val inputStream = c.inputStream
val totalSize = c.contentLength.toFloat() //size of apk
val buffer = ByteArray(1024)
var len1: Int
var per: Float
var downloaded = 0f
while (inputStream.read(buffer).also { len1 = it } != -1) {
fos.write(buffer, 0, len1)
downloaded += len1
per = (downloaded * 100 / totalSize)
publishProgress(per.toInt())
}
fos.close()
inputStream.close()
openNewVersion(outputFile.path)
flag = true
} catch (e: MalformedURLException) {
Log.e("DownloadApk", "Update Error: " + e.message)
flag = false
} catch (e: IOException) {
e.printStackTrace()
}
return flag
}
private fun openNewVersion(location: String) {
val intent = 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 fun getUriFromFile(filePath: String): Uri {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Uri.fromFile(File(filePath))
} else {
FileProvider.getUriForFile(
context,
context.packageName + ".provider",
File(filePath)
)
}
}
}
}

@ -0,0 +1,79 @@
package com.cyb3rko.techniklogger.update
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.androidnetworking.AndroidNetworking
import com.androidnetworking.error.ANError
import com.androidnetworking.interfaces.StringRequestListener
import com.cyb3rko.techniklogger.BuildConfig
import com.cyb3rko.techniklogger.MainActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import es.dmoral.toasty.Toasty
import java.nio.charset.StandardCharsets
internal object Updater {
internal fun updateCheck(activity: MainActivity) {
AndroidNetworking.initialize(activity)
AndroidNetworking.get("https://git.aldiserver.de/api/v1/repos/niko/technik-logger-app/contents/app/build.gradle")
.addHeaders("Authorization", "token d70a26aa455b25e60885ba5ff31cce231d454f82")
.doNotCacheResponse()
.build()
.getAsString(object : StringRequestListener {
override fun onResponse(response: String?) {
try {
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 ")
parts2 = parts[1].split("\n")
val neuesterVersionCode = parts2[0].toInt()
parts = parts2[1].split("\"")
parts2 = parts[1].split("\"")
val newestVersion = parts2[0]
if (BuildConfig.VERSION_CODE != neuesterVersionCode) {
Log.d("Technik-Logger Updater", "Update verfügbar: $newestVersion")
showDownloadDialog(activity, newestVersion)
ActivityCompat.requestPermissions(
activity, arrayOf(Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1
)
} else {
Log.d(activity.toString(), "App auf dem neuesten Stand")
}
} catch (e: Exception) {
Toasty.error(activity, "Update-Check fehlgeschlagen", Toasty.LENGTH_LONG).show()
}
}
override fun onError(anError: ANError?) {
Log.d(activity.toString(), "Update-Abfrage fehlgeschlagen: ${anError!!.errorBody.trimIndent()}")
}
})
}
private fun showDownloadDialog(context: Context, newestVersion: String) {
MaterialAlertDialogBuilder(context)
.setTitle("Neues Update verfügbar")
.setMessage("Das Update '$newestVersion' steht zum Download bereit!\n\nAktuell installierte Version: '${BuildConfig.VERSION_NAME}'")
.setPositiveButton("Herunterladen") { _, _ ->
downloadApk(context, newestVersion)
}
.show()
}
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"
DownloadApk(context).startDownloadingApk(link, "Technik-Logger $newestVersion")
} else {
Toasty.error(context, "Fehlende Berechtigung, erteile diese beim nächsten App-Start", Toasty.LENGTH_LONG).show()
}
}
}

@ -1 +0,0 @@
/build

@ -1,43 +0,0 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
}
android {
compileSdkVersion 31
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 19
targetSdkVersion 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled true
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:1.5.20"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
}

@ -1,21 +0,0 @@
# 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

@ -1,8 +0,0 @@
<?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>

@ -1,161 +0,0 @@
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.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloadApk extends Activity {
private static Context context;
private static String downloadUrl;
private static String fileName;
private static final String TAG = "DownloadApk";
public DownloadApk(Context context){
DownloadApk.context = context;
}
public void startDownloadingApk(String url){
downloadUrl = url;
fileName = url.split("Technik-Logger/")[1].replace("%20", " ");
if (downloadUrl != null){
new DownloadNewVersion(context).execute();
}
}
private static class DownloadNewVersion extends AsyncTask<String, Integer, Boolean> {
private ProgressDialog bar;
private final Context context;
DownloadNewVersion(Context context) {
this.context = context;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (bar == null){
bar = new ProgressDialog(context);
bar.setCancelable(false);
bar.setMessage("Lädt herunter...");
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 = "Beenden... ";
} else {
msg = "Herunterladen... " + progress[0] + "%";
}
bar.setMessage(msg);
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (bar.isShowing() && bar != null) {
bar.dismiss();
bar = null;
}
if (result) {
Toast.makeText(context,"Download erfolgreich", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context,"Download fehlgeschlagen", Toast.LENGTH_SHORT).show();
}
}
@Override
protected Boolean doInBackground(String... arg0) {
boolean flag = false;
try {
String destination = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/";
File outputFile = new File(destination + fileName);
if (outputFile.exists()) {
try {
openNewVersion(outputFile.getPath());
return true;
} catch (Exception ignored) {
}
}
outputFile.createNewFile();
File directory = new File(destination);
if (!directory.exists()) {
directory.mkdirs();
}
URL url = new URL(downloadUrl);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.connect();
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(outputFile.getPath());
flag = true;
} catch (MalformedURLException e) {
Log.e(TAG, "Update-Fehler: " + e.getMessage());
flag = false;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Update-Fehler: " + e.getMessage());
}
return flag;
}
}
private static void openNewVersion(String filePath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(getUriFromFile(filePath), "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 filePath) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return Uri.fromFile(new File(filePath));
} else {
return FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider",
new File(filePath));
}
}
}

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