From 23c5574e019f91c99c706f4e548eb032b19d3b11 Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Tue, 2 Jan 2024 17:24:05 +0000 Subject: [PATCH] Add visibility for the user during app restoring When the app is restoring the button's text will be constantly updated until the app is restored. Test: manual Bug: 304255818 Change-Id: Id5da0923e5f9f3e45889e10c017a3f3dc3f8bd95 --- res/values/strings.xml | 8 ++++ .../spa/app/appinfo/AppRestoreButton.kt | 42 ++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 491cd0aee7a..8a53344f487 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3945,6 +3945,14 @@ Archive Restore + + Restoring + + Restoring. + + Restoring.. + + Restoring... Total diff --git a/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt b/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt index 6596529812b..bf46e958e3e 100644 --- a/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt +++ b/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt @@ -27,9 +27,18 @@ import android.widget.Toast import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.CloudDownload import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch class AppRestoreButton(packageInfoPresenter: PackageInfoPresenter) { private companion object { @@ -43,6 +52,16 @@ class AppRestoreButton(packageInfoPresenter: PackageInfoPresenter) { private val packageName = packageInfoPresenter.packageName private val userHandle = UserHandle.of(packageInfoPresenter.userId) private var broadcastReceiverIsCreated = false + private lateinit var coroutineScope: CoroutineScope + private lateinit var updateButtonTextJob: Job + private val buttonTexts = intArrayOf( + R.string.restore, + R.string.restoring_step_one, + R.string.restoring_step_two, + R.string.restoring_step_three, + R.string.restoring_step_four, + ) + private var buttonTextIndexStateFlow = MutableStateFlow(0) @Composable fun getActionButton(app: ApplicationInfo): ActionButton { @@ -55,10 +74,17 @@ class AppRestoreButton(packageInfoPresenter: PackageInfoPresenter) { } broadcastReceiverIsCreated = true } + coroutineScope = rememberCoroutineScope() + if (app.isArchived && ::updateButtonTextJob.isInitialized && !updateButtonTextJob.isActive) { + buttonTextIndexStateFlow.value = 0 + } return ActionButton( - text = context.getString(R.string.restore), + text = context.getString( + buttonTexts[ + buttonTextIndexStateFlow.asStateFlow().collectAsStateWithLifecycle(0).value] + ), imageVector = Icons.Outlined.CloudDownload, - enabled = app.isArchived + enabled = app.isArchived && (!::updateButtonTextJob.isInitialized || !updateButtonTextJob.isActive) ) { onRestoreClicked(app) } } @@ -87,6 +113,18 @@ class AppRestoreButton(packageInfoPresenter: PackageInfoPresenter) { when (val unarchiveStatus = intent.getIntExtra(PackageInstaller.EXTRA_UNARCHIVE_STATUS, Int.MIN_VALUE)) { PackageInstaller.UNARCHIVAL_OK -> { + // updateButtonTextJob will be canceled automatically once + // AppButtonsPresenter#getActionButtons is triggered + updateButtonTextJob = coroutineScope.launch { + while (isActive) { + var index = buttonTextIndexStateFlow.value + index = (index + 1) % buttonTexts.size + // The initial state shouldn't be used here + if (index == 0) index++ + buttonTextIndexStateFlow.emit(index) + delay(1000) + } + } val appLabel = userPackageManager.getApplicationLabel(app) Toast.makeText( context,