Merge "Add visibility for the user during app restoring" into main
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 776e3f4..2b5117d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3967,6 +3967,14 @@
     <string name="archive">Archive</string>
     <!-- Manage applications, text label for button to restore an application. Restoring means installing the archived app. -->
     <string name="restore">Restore</string>
+    <!-- Manage applications, text label for button while the application is restoring. -->
+    <string name="restoring_step_one">Restoring</string>
+    <!-- Manage applications, text label for button while the application is restoring. -->
+    <string name="restoring_step_two">Restoring.</string>
+    <!-- Manage applications, text label for button while the application is restoring. -->
+    <string name="restoring_step_three">Restoring..</string>
+    <!-- Manage applications, text label for button while the application is restoring. -->
+    <string name="restoring_step_four">Restoring...</string>
     <!-- Manage applications, individual application info screen,label under Storage heading.  The total storage space taken up by this app. -->
     <string name="total_size_label">Total</string>
     <!-- Manage applications, individual application info screen, label under Storage heading. The amount of space taken up by the application itself (for example, the java compield files and things like that) -->
diff --git a/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt b/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt
index 6596529..bf46e95 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 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 @@
     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 @@
             }
             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 @@
         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,