aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2023-05-01 16:24:41 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2023-05-01 16:24:41 +0200
commitd6ad7d11d0dce92f927406379ced57af17b7bf6d (patch)
tree6c71f60a39b602b314811806a1bb512f84054535 /ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt
parentversion: bump (diff)
downloadwireguard-android-d6ad7d11d0dce92f927406379ced57af17b7bf6d.tar.xz
wireguard-android-d6ad7d11d0dce92f927406379ced57af17b7bf6d.zip
ui: handle update signatures
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt')
-rw-r--r--ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt156
1 files changed, 156 insertions, 0 deletions
diff --git a/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt b/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt
new file mode 100644
index 00000000..f7d32abb
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/updater/SnackbarUpdateShower.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.wireguard.android.updater
+
+import android.view.View
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.lifecycleScope
+import com.google.android.material.snackbar.BaseTransientBottomBar
+import com.google.android.material.snackbar.Snackbar
+import com.wireguard.android.R
+import com.wireguard.android.util.ErrorMessages
+import com.wireguard.android.util.QuantityFormatter
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlin.time.Duration.Companion.seconds
+
+object SnackbarUpdateShower {
+ private class SwapableSnackbar(activity: FragmentActivity, view: View, anchor: View?) {
+ val actionSnackbar = makeSnackbar(activity, view, anchor)
+ val statusSnackbar = makeSnackbar(activity, view, anchor)
+ var showingAction: Boolean = false
+ var showingStatus: Boolean = false
+
+ private fun makeSnackbar(activity: FragmentActivity, view: View, anchor: View?): Snackbar {
+ val snackbar = Snackbar.make(activity, view, "", Snackbar.LENGTH_INDEFINITE)
+ if (anchor != null)
+ snackbar.anchorView = anchor
+ snackbar.setTextMaxLines(6)
+ snackbar.behavior = object : BaseTransientBottomBar.Behavior() {
+ override fun canSwipeDismissView(child: View): Boolean {
+ return false
+ }
+ }
+ 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 ||
+ (snackbar == actionSnackbar && !showingAction) ||
+ (snackbar == statusSnackbar && !showingStatus)
+ )
+ return
+ activity.lifecycleScope.launch {
+ delay(5.seconds)
+ snackbar?.show()
+ }
+ }
+ })
+ return snackbar
+ }
+
+ fun showAction(text: String, action: String, listener: View.OnClickListener) {
+ if (showingStatus) {
+ showingStatus = false
+ statusSnackbar.dismiss()
+ }
+ actionSnackbar.setText(text)
+ actionSnackbar.setAction(action, listener)
+ if (!showingAction) {
+ actionSnackbar.show()
+ showingAction = true
+ }
+ }
+
+ fun showText(text: String) {
+ if (showingAction) {
+ showingAction = false
+ actionSnackbar.dismiss()
+ }
+ statusSnackbar.setText(text)
+ if (!showingStatus) {
+ statusSnackbar.show()
+ showingStatus = true
+ }
+ }
+
+ fun dismiss() {
+ actionSnackbar.dismiss()
+ statusSnackbar.dismiss()
+ showingAction = false
+ showingStatus = false
+ }
+ }
+
+ fun attachToActivity(activity: FragmentActivity, view: View, anchor: View?) {
+ val snackbar = SwapableSnackbar(activity, view, anchor)
+ val context = activity.applicationContext
+
+ var lastUserIntervention: Updater.Progress.NeedsUserIntervention? = null
+ val intentLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ lastUserIntervention?.markAsDone()
+ }
+
+ Updater.state.onEach { progress ->
+ when (progress) {
+ is Updater.Progress.Complete ->
+ snackbar.dismiss()
+
+ is Updater.Progress.Available ->
+ snackbar.showAction(
+ context.getString(R.string.updater_avalable),
+ context.getString(R.string.updater_action)
+ ) {
+ progress.update()
+ }
+
+ is Updater.Progress.NeedsUserIntervention -> {
+ lastUserIntervention = progress
+ intentLauncher.launch(progress.intent)
+ }
+
+ is Updater.Progress.Installing ->
+ snackbar.showText(context.getString(R.string.updater_installing))
+
+ is Updater.Progress.Rechecking ->
+ snackbar.showText(context.getString(R.string.updater_rechecking))
+
+ is Updater.Progress.Downloading -> {
+ if (progress.bytesTotal != 0UL) {
+ snackbar.showText(
+ context.getString(
+ R.string.updater_download_progress,
+ QuantityFormatter.formatBytes(progress.bytesDownloaded.toLong()),
+ QuantityFormatter.formatBytes(progress.bytesTotal.toLong()),
+ progress.bytesDownloaded.toFloat() * 100.0 / progress.bytesTotal.toFloat()
+ )
+ )
+ } else {
+ snackbar.showText(
+ context.getString(
+ R.string.updater_download_progress_nototal,
+ QuantityFormatter.formatBytes(progress.bytesDownloaded.toLong())
+ )
+ )
+ }
+ }
+
+ is Updater.Progress.Failure -> {
+ snackbar.showText(
+ context.getString(
+ R.string.updater_failure,
+ ErrorMessages[progress.error]
+ )
+ )
+ delay(5.seconds)
+ progress.retry()
+ }
+ }
+ }.launchIn(activity.lifecycleScope)
+ }
+} \ No newline at end of file