diff options
Diffstat (limited to 'ui/src')
4 files changed, 61 insertions, 7 deletions
diff --git a/ui/src/googleplay/AndroidManifest.xml b/ui/src/googleplay/AndroidManifest.xml new file mode 100644 index 00000000..6d64f732 --- /dev/null +++ b/ui/src/googleplay/AndroidManifest.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> + + <uses-permission + android:name="android.permission.REQUEST_INSTALL_PACKAGES" + tools:node="remove" /> +</manifest> diff --git a/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt b/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt index b566a0cf..8c1a8124 100644 --- a/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt +++ b/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt @@ -5,7 +5,10 @@ package com.wireguard.android.updater +import android.content.Intent +import android.net.Uri import android.view.View +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope @@ -27,10 +30,11 @@ class SnackbarUpdateShower(private val fragment: Fragment) { } private class SwapableSnackbar(fragment: Fragment, view: View, anchor: View?) { - val actionSnackbar = makeSnackbar(fragment, view, anchor) - val statusSnackbar = makeSnackbar(fragment, view, anchor) - var showingAction: Boolean = false - var showingStatus: Boolean = false + private val actionSnackbar = makeSnackbar(fragment, view, anchor) + private val statusSnackbar = makeSnackbar(fragment, view, anchor) + private var showingAction: Boolean = false + private var showingStatus: Boolean = false + private var permanentAction: Boolean = false private fun makeSnackbar(fragment: Fragment, view: View, anchor: View?): Snackbar { val snackbar = Snackbar.make(fragment.requireContext(), view, "", Snackbar.LENGTH_INDEFINITE) @@ -45,7 +49,7 @@ class SnackbarUpdateShower(private val fragment: Fragment) { snackbar.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() { override fun onDismissed(snackbar: Snackbar?, @DismissEvent event: Int) { super.onDismissed(snackbar, event) - if (event == DISMISS_EVENT_MANUAL || event == DISMISS_EVENT_ACTION || + if (event == DISMISS_EVENT_MANUAL || (event == DISMISS_EVENT_ACTION && !permanentAction) || (snackbar == actionSnackbar && !showingAction) || (snackbar == statusSnackbar && !showingStatus) ) return @@ -58,11 +62,12 @@ class SnackbarUpdateShower(private val fragment: Fragment) { return snackbar } - fun showAction(text: String, action: String, listener: View.OnClickListener) { + fun showAction(text: String, action: String, permanent: Boolean = false, listener: View.OnClickListener) { if (showingStatus) { showingStatus = false statusSnackbar.dismiss() } + permanentAction = permanent actionSnackbar.setText(text) actionSnackbar.setAction(action, listener) if (!showingAction) { @@ -141,6 +146,18 @@ class SnackbarUpdateShower(private val fragment: Fragment) { delay(5.seconds) progress.retry() } + + is Updater.Progress.Corrupt -> { + snackbar.showAction(context.getString(R.string.updater_corrupt), context.getString(R.string.updater_corrupt_navigate), true) { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(progress.downloadUrl) + try { + context.startActivity(intent) + } catch (e: Throwable) { + Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show() + } + } + } } }.launchIn(fragment.lifecycleScope) } diff --git a/ui/src/main/java/com/wireguard/android/updater/Updater.kt b/ui/src/main/java/com/wireguard/android/updater/Updater.kt index fabfae92..928028fd 100644 --- a/ui/src/main/java/com/wireguard/android/updater/Updater.kt +++ b/ui/src/main/java/com/wireguard/android/updater/Updater.kt @@ -4,12 +4,14 @@ */ package com.wireguard.android.updater +import android.Manifest import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageInstaller +import android.content.pm.PackageManager import android.os.Build import android.util.Base64 import android.util.Log @@ -116,6 +118,11 @@ object Updater { } } } + + class Corrupt(private val betterFile: String?) : Progress() { + val downloadUrl: String + get() = UPDATE_URL_FMT.format(betterFile ?: "") + } } private val mutableState = MutableStateFlow<Progress>(Progress.Complete) @@ -357,9 +364,29 @@ object Updater { } fun monitorForUpdates() { - if (installerIsGooglePlay(Application.get())) + val context = Application.get() + + if (installerIsGooglePlay(context)) return + if (!if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + @Suppress("DEPRECATION") + context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS) + } else { + context.packageManager.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong())) + }.requestedPermissions.contains(Manifest.permission.REQUEST_INSTALL_PACKAGES) + ) { + updaterScope.launch { + val update = try { + checkForUpdates() + } catch (_: Throwable) { + null + } + emitProgress(Progress.Corrupt(update?.fileName)) + } + return + } + updaterScope.launch { if (UserKnobs.updaterNewerVersionSeen.firstOrNull()?.let { Version(it) > CURRENT_VERSION } == true) return@launch diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 66e35c98..7d7456bc 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -236,6 +236,8 @@ <string name="updater_download_progress_nototal">Downloading update: %s</string> <string name="updater_installing">Installing update…</string> <string name="updater_failure">Update failure: %s. Will retry momentarily…</string> + <string name="updater_corrupt">This application is corrupt. Please re-download it.</string> + <string name="updater_corrupt_navigate">Open Website</string> <string name="version_summary">%1$s backend %2$s</string> <string name="version_summary_checking">Checking %s backend version</string> <string name="version_summary_unknown">Unknown %s version</string> |