diff --git a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt index 913da65a03a..0eef9c39f1b 100644 --- a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt +++ b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt @@ -33,10 +33,15 @@ import com.android.settings.R import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map -class AppArchiveButton(packageInfoPresenter: PackageInfoPresenter) { +class AppArchiveButton( + packageInfoPresenter: PackageInfoPresenter, + private val isHibernationSwitchEnabledStateFlow: MutableStateFlow, +) { private companion object { private const val LOG_TAG = "AppArchiveButton" private const val INTENT_ACTION = "com.android.settings.archive.action" @@ -65,18 +70,20 @@ class AppArchiveButton(packageInfoPresenter: PackageInfoPresenter) { text = context.getString(R.string.archive), imageVector = Icons.Outlined.CloudUpload, enabled = remember(app) { - flow { - emit( - app.isActionButtonEnabled() && appButtonRepository.isAllowUninstallOrArchive( - context, - app - ) - ) + isHibernationSwitchEnabledStateFlow.asStateFlow().map { + it && isActionButtonEnabledForApp(app) }.flowOn(Dispatchers.Default) }.collectAsStateWithLifecycle(false).value ) { onArchiveClicked(app) } } + private fun isActionButtonEnabledForApp(app: ApplicationInfo): Boolean { + return app.isActionButtonEnabled() && appButtonRepository.isAllowUninstallOrArchive( + context, + app + ) + } + private fun ApplicationInfo.isActionButtonEnabled(): Boolean { return !isArchived && userPackageManager.isAppArchivable(packageName) diff --git a/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt b/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt index f01c31c01a6..5fa943602e2 100644 --- a/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt +++ b/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt @@ -23,6 +23,7 @@ import android.content.om.OverlayManager import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import android.util.Log import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.Utils diff --git a/src/com/android/settings/spa/app/appinfo/AppButtons.kt b/src/com/android/settings/spa/app/appinfo/AppButtons.kt index c0fa313c365..dcce1d91fc2 100644 --- a/src/com/android/settings/spa/app/appinfo/AppButtons.kt +++ b/src/com/android/settings/spa/app/appinfo/AppButtons.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settingslib.applications.AppUtils import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spa.widget.button.ActionButtons +import kotlinx.coroutines.flow.MutableStateFlow @Composable /** @@ -32,10 +33,17 @@ import com.android.settingslib.spa.widget.button.ActionButtons */ fun AppButtons( packageInfoPresenter: PackageInfoPresenter, + isHibernationSwitchEnabledStateFlow: MutableStateFlow, featureFlags: FeatureFlags = FeatureFlagsImpl() ) { if (remember(packageInfoPresenter) { packageInfoPresenter.isMainlineModule() }) return - val presenter = remember { AppButtonsPresenter(packageInfoPresenter, featureFlags) } + val presenter = remember { + AppButtonsPresenter( + packageInfoPresenter, + isHibernationSwitchEnabledStateFlow, + featureFlags + ) + } ActionButtons(actionButtons = presenter.getActionButtons()) } @@ -44,6 +52,7 @@ private fun PackageInfoPresenter.isMainlineModule(): Boolean = private class AppButtonsPresenter( private val packageInfoPresenter: PackageInfoPresenter, + isHibernationSwitchEnabledStateFlow: MutableStateFlow, private val featureFlags: FeatureFlags ) { private val appLaunchButton = AppLaunchButton(packageInfoPresenter) @@ -52,7 +61,8 @@ private class AppButtonsPresenter( private val appUninstallButton = AppUninstallButton(packageInfoPresenter) private val appClearButton = AppClearButton(packageInfoPresenter) private val appForceStopButton = AppForceStopButton(packageInfoPresenter) - private val appArchiveButton = AppArchiveButton(packageInfoPresenter) + private val appArchiveButton = + AppArchiveButton(packageInfoPresenter, isHibernationSwitchEnabledStateFlow) private val appRestoreButton = AppRestoreButton(packageInfoPresenter) @Composable diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index 92918927f22..dba61848501 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -50,6 +50,7 @@ import com.android.settingslib.spa.widget.scaffold.RegularScaffold import com.android.settingslib.spa.widget.ui.Category import com.android.settingslib.spaprivileged.model.app.toRoute import com.android.settingslib.spaprivileged.template.app.AppInfoProvider +import kotlinx.coroutines.flow.MutableStateFlow private const val PACKAGE_NAME = "packageName" private const val USER_ID = "userId" @@ -133,10 +134,11 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { val packageInfo = packageInfoState.value ?: return@RegularScaffold val app = packageInfo.applicationInfo ?: return@RegularScaffold val appInfoProvider = remember(packageInfo) { AppInfoProvider(packageInfo) } + val isHibernationSwitchEnabledStateFlow = MutableStateFlow(false) appInfoProvider.AppInfo() - AppButtons(packageInfoPresenter) + AppButtons(packageInfoPresenter, isHibernationSwitchEnabledStateFlow) AppSettingsPreference(app) AppAllServicesPreference(app) @@ -152,7 +154,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) { DefaultAppShortcuts(app) Category(title = stringResource(R.string.unused_apps_category)) { - HibernationSwitchPreference(app) + HibernationSwitchPreference(app, isHibernationSwitchEnabledStateFlow) } Category(title = stringResource(R.string.advanced_apps)) { diff --git a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt index 324fa061c43..4f472662120 100644 --- a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt +++ b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt @@ -48,11 +48,15 @@ import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext @Composable -fun HibernationSwitchPreference(app: ApplicationInfo) { +fun HibernationSwitchPreference( + app: ApplicationInfo, + isHibernationSwitchEnabledStateFlow: MutableStateFlow +) { val context = LocalContext.current val presenter = remember(app) { HibernationSwitchPresenter(context, app) } if (!presenter.isAvailable()) return @@ -73,7 +77,14 @@ fun HibernationSwitchPreference(app: ApplicationInfo) { context.getString(R.string.unused_apps_switch_summary) } override val changeable = { isEligibleState } - override val checked = { if (changeable()) isCheckedState.value else false } + override val checked = { + val result = if (changeable()) isCheckedState.value else false + result.also { isChecked -> + isChecked?.let { + isHibernationSwitchEnabledStateFlow.value = it + } + } + } override val onCheckedChange = presenter::onCheckedChange } }) diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt index cc5e365ac3a..df1f15302dd 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt @@ -26,8 +26,10 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R +import com.android.settingslib.spa.testutils.delay import com.android.settingslib.spa.widget.button.ActionButton import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow import org.junit.Before import org.junit.Rule import org.junit.Test @@ -52,6 +54,8 @@ class AppArchiveButtonTest { private val packageInstaller = mock() + private val isHibernationSwitchEnabledStateFlow = MutableStateFlow(true) + private lateinit var appArchiveButton: AppArchiveButton @Before @@ -60,7 +64,7 @@ class AppArchiveButtonTest { whenever(packageInfoPresenter.userPackageManager).thenReturn(userPackageManager) whenever(userPackageManager.packageInstaller).thenReturn(packageInstaller) whenever(packageInfoPresenter.packageName).thenReturn(PACKAGE_NAME) - appArchiveButton = AppArchiveButton(packageInfoPresenter) + appArchiveButton = AppArchiveButton(packageInfoPresenter, isHibernationSwitchEnabledStateFlow) } @Test @@ -89,6 +93,20 @@ class AppArchiveButtonTest { assertThat(actionButton.enabled).isFalse() } + @Test + fun appArchiveButton_whenIsHibernationSwitchDisabled_isDisabled() { + val app = ApplicationInfo().apply { + packageName = PACKAGE_NAME + isArchived = false + flags = ApplicationInfo.FLAG_INSTALLED + } + whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true) + isHibernationSwitchEnabledStateFlow.value = false + val enabledActionButton = setContent(app) + + assertThat(enabledActionButton.enabled).isFalse() + } + @Test fun appArchiveButton_displaysRightTextAndIcon() { val app = ApplicationInfo().apply { @@ -126,6 +144,7 @@ class AppArchiveButtonTest { composeTestRule.setContent { actionButton = appArchiveButton.getActionButton(app) } + composeTestRule.delay() return actionButton } diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt index 6d22c92d98d..733e1a4150c 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt @@ -68,6 +68,7 @@ class AppButtonsTest { private lateinit var packageInstaller: PackageInstaller private val featureFlags = FakeFeatureFlagsImpl() + private val isHibernationSwitchEnabledStateFlow = MutableStateFlow(true) @Before fun setUp() { @@ -175,7 +176,7 @@ class AppButtonsTest { private fun setContent(packageInfo: PackageInfo = PACKAGE_INFO) { whenever(packageInfoPresenter.flow).thenReturn(MutableStateFlow(packageInfo)) composeTestRule.setContent { - AppButtons(packageInfoPresenter, featureFlags) + AppButtons(packageInfoPresenter, isHibernationSwitchEnabledStateFlow, featureFlags) } composeTestRule.delay() diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt index 57e31da4538..cc2c1e106e1 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreferenceTest.kt @@ -70,6 +70,7 @@ import org.mockito.Spy import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import java.util.function.IntConsumer +import kotlinx.coroutines.flow.MutableStateFlow import org.mockito.Mockito.`when` as whenever @RunWith(AndroidJUnit4::class) @@ -99,6 +100,8 @@ class HibernationSwitchPreferenceTest { private val hibernationTargetsPreSConfig = TestDeviceConfig(NAMESPACE_APP_HIBERNATION, PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS) + private val isHibernationSwitchEnabledStateFlow = MutableStateFlow(true) + @Before fun setUp() { hibernationEnabledConfig.override(true) @@ -234,7 +237,7 @@ class HibernationSwitchPreferenceTest { private fun setContent(app: ApplicationInfo = TARGET_R_APP) { composeTestRule.setContent { CompositionLocalProvider(LocalContext provides context) { - HibernationSwitchPreference(app) + HibernationSwitchPreference(app, isHibernationSwitchEnabledStateFlow) } } }