From ebcd11d00cf28b978f38c437b3228f7235b5d5ab Mon Sep 17 00:00:00 2001 From: Bnyro Date: Mon, 18 Jul 2022 19:15:35 +0200 Subject: [PATCH] support for silent updates --- app/src/main/AndroidManifest.xml | 8 +- .../main/java/com/github/libretube/MyApp.kt | 8 ++ .../github/libretube/dialogs/UpdateDialog.kt | 46 ++++++----- .../com/github/libretube/obj/VersionInfo.kt | 7 -- .../libretube/preferences/MainSettings.kt | 13 ++- .../libretube/services/DownloadService.kt | 5 +- .../libretube/services/UpdateService.kt | 79 +++++++++++++++++++ .../{util => update}/UpdateChecker.kt | 20 ++--- app/src/main/res/values/strings.xml | 1 + 9 files changed, 137 insertions(+), 50 deletions(-) delete mode 100644 app/src/main/java/com/github/libretube/obj/VersionInfo.kt create mode 100644 app/src/main/java/com/github/libretube/services/UpdateService.kt rename app/src/main/java/com/github/libretube/{util => update}/UpdateChecker.kt (71%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 44a9db690..e0ebe4a6b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -277,8 +277,12 @@ + android:exported="false" /> + + \ No newline at end of file diff --git a/app/src/main/java/com/github/libretube/MyApp.kt b/app/src/main/java/com/github/libretube/MyApp.kt index c2bcc9c57..1baa1f2dc 100644 --- a/app/src/main/java/com/github/libretube/MyApp.kt +++ b/app/src/main/java/com/github/libretube/MyApp.kt @@ -4,6 +4,8 @@ import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager import android.os.Build +import android.os.StrictMode +import android.os.StrictMode.VmPolicy import com.github.libretube.preferences.PreferenceHelper class MyApp : Application() { @@ -19,6 +21,12 @@ class MyApp : Application() { * set the applicationContext as context for the [PreferenceHelper] */ PreferenceHelper.setContext(applicationContext) + + /** + * bypassing fileUriExposedException, see https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed + */ + val builder = VmPolicy.Builder() + StrictMode.setVmPolicy(builder.build()) } /** diff --git a/app/src/main/java/com/github/libretube/dialogs/UpdateDialog.kt b/app/src/main/java/com/github/libretube/dialogs/UpdateDialog.kt index 7b35766c3..48f102639 100644 --- a/app/src/main/java/com/github/libretube/dialogs/UpdateDialog.kt +++ b/app/src/main/java/com/github/libretube/dialogs/UpdateDialog.kt @@ -3,43 +3,51 @@ package com.github.libretube.dialogs import android.app.Dialog import android.content.Intent import android.net.Uri +import android.os.Build import android.os.Bundle +import android.util.Log import androidx.fragment.app.DialogFragment import com.github.libretube.R +import com.github.libretube.services.UpdateService +import com.github.libretube.update.UpdateInfo import com.google.android.material.dialog.MaterialAlertDialogBuilder -class UpdateAvailableDialog( - private val versionTag: String, - private val updateLink: String +class UpdateDialog( + private val updateInfo: UpdateInfo ) : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return activity?.let { MaterialAlertDialogBuilder(requireContext()) - .setTitle(context?.getString(R.string.update_available, versionTag)) - .setMessage(context?.getString(R.string.update_available_text)) + .setTitle(context?.getString(R.string.update_available, updateInfo.name)) + .setMessage(context?.getString(R.string.update_now)) .setNegativeButton(context?.getString(R.string.cancel)) { _, _ -> dismiss() } .setPositiveButton(context?.getString(R.string.okay)) { _, _ -> - val uri = Uri.parse(updateLink) - val intent = Intent(Intent.ACTION_VIEW).setData(uri) - startActivity(intent) + val downloadUrl = getDownloadUrl(updateInfo) + Log.i("downloadUrl", downloadUrl.toString()) + if (downloadUrl != null) { + val intent = Intent(context, UpdateService::class.java) + intent.putExtra("downloadUrl", downloadUrl) + context?.startService(intent) + } else { + val uri = Uri.parse(updateInfo.html_url) + val intent = Intent(Intent.ACTION_VIEW).setData(uri) + startActivity(intent) + } } .create() } ?: throw IllegalStateException("Activity cannot be null") } -} -class NoUpdateAvailableDialog : DialogFragment() { - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return activity?.let { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(context?.getString(R.string.app_uptodate)) - .setMessage(context?.getString(R.string.no_update_available)) - .setPositiveButton(context?.getString(R.string.okay)) { _, _ -> } - .create() - } ?: throw IllegalStateException("Activity cannot be null") + private fun getDownloadUrl(updateInfo: UpdateInfo): String? { + val supportedArchitectures = Build.SUPPORTED_ABIS + supportedArchitectures.forEach { arch -> + updateInfo.assets.forEach { + if (it.browser_download_url.contains(arch)) return it.browser_download_url + } + } + return null } } diff --git a/app/src/main/java/com/github/libretube/obj/VersionInfo.kt b/app/src/main/java/com/github/libretube/obj/VersionInfo.kt deleted file mode 100644 index e52d7ef79..000000000 --- a/app/src/main/java/com/github/libretube/obj/VersionInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.libretube.obj - -// data class for the update info, required to return the data -data class VersionInfo( - val updateUrl: String, - val tagName: String -) diff --git a/app/src/main/java/com/github/libretube/preferences/MainSettings.kt b/app/src/main/java/com/github/libretube/preferences/MainSettings.kt index 2006db60d..ff1b50486 100644 --- a/app/src/main/java/com/github/libretube/preferences/MainSettings.kt +++ b/app/src/main/java/com/github/libretube/preferences/MainSettings.kt @@ -10,9 +10,9 @@ import com.github.libretube.BuildConfig import com.github.libretube.R import com.github.libretube.activities.SettingsActivity import com.github.libretube.dialogs.RequireRestartDialog -import com.github.libretube.dialogs.UpdateAvailableDialog +import com.github.libretube.dialogs.UpdateDialog +import com.github.libretube.update.checkUpdate import com.github.libretube.util.ThemeHelper -import com.github.libretube.util.checkUpdate import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -93,13 +93,10 @@ class MainSettings : PreferenceFragmentCompat() { update?.setOnPreferenceClickListener { CoroutineScope(Dispatchers.IO).launch { // check for update - val versionInfo = checkUpdate() - if (versionInfo?.tagName != "" && BuildConfig.VERSION_NAME != versionInfo?.tagName) { + val updateInfo = checkUpdate() + if (updateInfo?.name != "" && BuildConfig.VERSION_NAME != updateInfo?.name) { // show the UpdateAvailableDialog if there's an update available - val updateAvailableDialog = UpdateAvailableDialog( - versionInfo!!.tagName, - versionInfo.updateUrl - ) + val updateAvailableDialog = UpdateDialog(updateInfo!!) updateAvailableDialog.show(childFragmentManager, "UpdateAvailableDialog") } else { // otherwise show the no update available snackBar diff --git a/app/src/main/java/com/github/libretube/services/DownloadService.kt b/app/src/main/java/com/github/libretube/services/DownloadService.kt index f01a9a903..edfde2653 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -164,7 +164,10 @@ class DownloadService : Service() { onDestroy() } } else { - muxDownloadedMedia() + try { + muxDownloadedMedia() + } catch (e: Exception) { + } } } } diff --git a/app/src/main/java/com/github/libretube/services/UpdateService.kt b/app/src/main/java/com/github/libretube/services/UpdateService.kt new file mode 100644 index 000000000..3e161074e --- /dev/null +++ b/app/src/main/java/com/github/libretube/services/UpdateService.kt @@ -0,0 +1,79 @@ +package com.github.libretube.services + +import android.app.DownloadManager +import android.app.Service +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.Uri +import android.os.Environment +import android.os.IBinder +import java.io.File + +class UpdateService : Service() { + private val TAG = "UpdateService" + private lateinit var downloadUrl: String + private var downloadId: Long = -1 + private lateinit var file: File + private lateinit var downloadManager: DownloadManager + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + downloadUrl = intent?.getStringExtra("downloadUrl")!! + + downloadApk(downloadUrl) + + return super.onStartCommand(intent, flags, startId) + } + + private fun downloadApk(downloadUrl: String) { + val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + // val dir = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + file = File(dir, "release.apk") + + val request: DownloadManager.Request = + DownloadManager.Request(Uri.parse(downloadUrl)) + .setTitle("Downloading APK ...") + .setDescription("") + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) + .setDestinationUri(Uri.fromFile(file)) + .setAllowedOverMetered(true) + .setAllowedOverRoaming(true) + + downloadManager = + applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager + + downloadId = downloadManager.enqueue(request) + + // listener for the download to end + registerReceiver( + onDownloadComplete, + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) + ) + } + + private val onDownloadComplete: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) + if (downloadId == id) { + // install the apk after download finished + val installIntent = Intent(Intent.ACTION_VIEW) + installIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + installIntent.setDataAndType( + Uri.fromFile(file), + downloadManager.getMimeTypeForDownloadedFile(downloadId) + ) + startActivity(installIntent) + } + } + } + + override fun onDestroy() { + unregisterReceiver(onDownloadComplete) + super.onDestroy() + } + + override fun onBind(p0: Intent?): IBinder? { + TODO("Not yet implemented") + } +} diff --git a/app/src/main/java/com/github/libretube/util/UpdateChecker.kt b/app/src/main/java/com/github/libretube/update/UpdateChecker.kt similarity index 71% rename from app/src/main/java/com/github/libretube/util/UpdateChecker.kt rename to app/src/main/java/com/github/libretube/update/UpdateChecker.kt index 1620f0a6b..9d46c4981 100644 --- a/app/src/main/java/com/github/libretube/util/UpdateChecker.kt +++ b/app/src/main/java/com/github/libretube/update/UpdateChecker.kt @@ -1,16 +1,14 @@ -package com.github.libretube.util +package com.github.libretube.update import com.github.libretube.GITHUB_API_URL -import com.github.libretube.obj.VersionInfo -import com.github.libretube.update.UpdateInfo import com.google.gson.Gson import java.io.BufferedReader import java.io.InputStreamReader import java.net.URL import javax.net.ssl.HttpsURLConnection -fun checkUpdate(): VersionInfo? { - var versionInfo: VersionInfo? = null +fun checkUpdate(): UpdateInfo? { + var versionInfo: UpdateInfo? = null // run http request as thread to make it async val thread = Thread { // otherwise crashes without internet @@ -27,22 +25,18 @@ fun checkUpdate(): VersionInfo? { return versionInfo } -fun getUpdateInfo(): VersionInfo? { +fun getUpdateInfo(): UpdateInfo? { val latest = URL(GITHUB_API_URL) val json = StringBuilder() val urlConnection: HttpsURLConnection? urlConnection = latest.openConnection() as HttpsURLConnection - val br = BufferedReader(InputStreamReader(urlConnection.inputStream)) + // read json + val br = BufferedReader(InputStreamReader(urlConnection.inputStream)) var line: String? while (br.readLine().also { line = it } != null) json.append(line) // Parse and return the json data val gson = Gson() - val updateInfo = gson.fromJson(json.toString(), UpdateInfo::class.java) - - return VersionInfo( - updateInfo.html_url, - updateInfo.name - ) + return gson.fromJson(json.toString(), UpdateInfo::class.java) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 39684473a..2e780a185 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -252,4 +252,5 @@ System caption style Captions None + Do you want to update the app now? \ No newline at end of file