ui: bifurcate only parts of release but nag about wrong context

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2023-05-10 18:34:21 +02:00
parent f829b8abe0
commit 4156b83b84
5 changed files with 65 additions and 7 deletions

View File

@ -49,6 +49,10 @@ android {
applicationIdSuffix = ".debug" applicationIdSuffix = ".debug"
versionNameSuffix = "-debug" versionNameSuffix = "-debug"
} }
create("googleplay") {
initWith(getByName("release"))
matchingFallbacks += "release"
}
} }
lint { lint {
disable += "LongLogTag" disable += "LongLogTag"

View File

@ -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>

View File

@ -5,7 +5,10 @@
package com.wireguard.android.updater package com.wireguard.android.updater
import android.content.Intent
import android.net.Uri
import android.view.View import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -27,10 +30,11 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
} }
private class SwapableSnackbar(fragment: Fragment, view: View, anchor: View?) { private class SwapableSnackbar(fragment: Fragment, view: View, anchor: View?) {
val actionSnackbar = makeSnackbar(fragment, view, anchor) private val actionSnackbar = makeSnackbar(fragment, view, anchor)
val statusSnackbar = makeSnackbar(fragment, view, anchor) private val statusSnackbar = makeSnackbar(fragment, view, anchor)
var showingAction: Boolean = false private var showingAction: Boolean = false
var showingStatus: Boolean = false private var showingStatus: Boolean = false
private var permanentAction: Boolean = false
private fun makeSnackbar(fragment: Fragment, view: View, anchor: View?): Snackbar { private fun makeSnackbar(fragment: Fragment, view: View, anchor: View?): Snackbar {
val snackbar = Snackbar.make(fragment.requireContext(), view, "", Snackbar.LENGTH_INDEFINITE) 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>() { snackbar.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(snackbar: Snackbar?, @DismissEvent event: Int) { override fun onDismissed(snackbar: Snackbar?, @DismissEvent event: Int) {
super.onDismissed(snackbar, event) 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) (snackbar == actionSnackbar && !showingAction) || (snackbar == statusSnackbar && !showingStatus)
) )
return return
@ -58,11 +62,12 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
return snackbar 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) { if (showingStatus) {
showingStatus = false showingStatus = false
statusSnackbar.dismiss() statusSnackbar.dismiss()
} }
permanentAction = permanent
actionSnackbar.setText(text) actionSnackbar.setText(text)
actionSnackbar.setAction(action, listener) actionSnackbar.setAction(action, listener)
if (!showingAction) { if (!showingAction) {
@ -141,6 +146,18 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
delay(5.seconds) delay(5.seconds)
progress.retry() 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) }.launchIn(fragment.lifecycleScope)
} }

View File

@ -4,12 +4,14 @@
*/ */
package com.wireguard.android.updater package com.wireguard.android.updater
import android.Manifest
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.Base64 import android.util.Base64
import android.util.Log 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) private val mutableState = MutableStateFlow<Progress>(Progress.Complete)
@ -357,9 +364,29 @@ object Updater {
} }
fun monitorForUpdates() { fun monitorForUpdates() {
if (installerIsGooglePlay(Application.get())) val context = Application.get()
if (installerIsGooglePlay(context))
return 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 { updaterScope.launch {
if (UserKnobs.updaterNewerVersionSeen.firstOrNull()?.let { Version(it) > CURRENT_VERSION } == true) if (UserKnobs.updaterNewerVersionSeen.firstOrNull()?.let { Version(it) > CURRENT_VERSION } == true)
return@launch return@launch

View File

@ -236,6 +236,8 @@
<string name="updater_download_progress_nototal">Downloading update: %s</string> <string name="updater_download_progress_nototal">Downloading update: %s</string>
<string name="updater_installing">Installing update…</string> <string name="updater_installing">Installing update…</string>
<string name="updater_failure">Update failure: %s. Will retry momentarily…</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">%1$s backend %2$s</string>
<string name="version_summary_checking">Checking %s backend version</string> <string name="version_summary_checking">Checking %s backend version</string>
<string name="version_summary_unknown">Unknown %s version</string> <string name="version_summary_unknown">Unknown %s version</string>