Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 750c6072 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Use SettingsAlertDialog for app button dialogs

Use the spa standard widget SettingsAlertDialog to unify the dialog
style.

Bug: 236346018
Test: Manually with Settings
Test: Unit test
Change-Id: Idb231600e38ec7b0244baa5101da912ed2b9fd3c
parent 7acbaf2d
Loading
Loading
Loading
Loading
+6 −15
Original line number Original line Diff line number Diff line
@@ -24,14 +24,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.applications.AppUtils
import com.android.settingslib.applications.AppUtils
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButtons
import com.android.settingslib.spa.widget.button.ActionButtons
import kotlinx.coroutines.flow.map


@Composable
@Composable
fun AppButtons(packageInfoPresenter: PackageInfoPresenter) {
fun AppButtons(packageInfoPresenter: PackageInfoPresenter) {
    if (remember(packageInfoPresenter) { packageInfoPresenter.isMainlineModule() }) return
    if (remember(packageInfoPresenter) { packageInfoPresenter.isMainlineModule() }) return
    val presenter = remember { AppButtonsPresenter(packageInfoPresenter) }
    val presenter = remember { AppButtonsPresenter(packageInfoPresenter) }
    presenter.Dialogs()
    ActionButtons(actionButtons = presenter.getActionButtons())
    ActionButtons(actionButtons = presenter.rememberActionsButtons().value)
}
}


private fun PackageInfoPresenter.isMainlineModule(): Boolean =
private fun PackageInfoPresenter.isMainlineModule(): Boolean =
@@ -47,12 +45,12 @@ private class AppButtonsPresenter(private val packageInfoPresenter: PackageInfoP


    @OptIn(ExperimentalLifecycleComposeApi::class)
    @OptIn(ExperimentalLifecycleComposeApi::class)
    @Composable
    @Composable
    fun rememberActionsButtons() = remember {
    fun getActionButtons() =
        packageInfoPresenter.flow.map { packageInfo ->
        packageInfoPresenter.flow.collectAsStateWithLifecycle(initialValue = null).value?.let {
            if (packageInfo != null) getActionButtons(packageInfo.applicationInfo) else emptyList()
            getActionButtons(it.applicationInfo)
        }
        } ?: emptyList()
    }.collectAsStateWithLifecycle(initialValue = emptyList())


    @Composable
    private fun getActionButtons(app: ApplicationInfo): List<ActionButton> = listOfNotNull(
    private fun getActionButtons(app: ApplicationInfo): List<ActionButton> = listOfNotNull(
        appLaunchButton.getActionButton(app),
        appLaunchButton.getActionButton(app),
        appInstallButton.getActionButton(app),
        appInstallButton.getActionButton(app),
@@ -61,11 +59,4 @@ private class AppButtonsPresenter(private val packageInfoPresenter: PackageInfoP
        appClearButton.getActionButton(app),
        appClearButton.getActionButton(app),
        appForceStopButton.getActionButton(app),
        appForceStopButton.getActionButton(app),
    )
    )

    @Composable
    fun Dialogs() {
        appDisableButton.DisableConfirmDialog()
        appClearButton.ClearConfirmDialog()
        appForceStopButton.ForceStopConfirmDialog()
    }
}
}
+20 −37
Original line number Original line Diff line number Diff line
@@ -19,61 +19,44 @@ package com.android.settings.spa.app.appinfo
import android.content.pm.ApplicationInfo
import android.content.pm.ApplicationInfo
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.R
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter


class AppClearButton(
class AppClearButton(
    private val packageInfoPresenter: PackageInfoPresenter,
    private val packageInfoPresenter: PackageInfoPresenter,
) {
) {
    private val context = packageInfoPresenter.context
    private val context = packageInfoPresenter.context


    private var openConfirmDialog by mutableStateOf(false)
    @Composable

    fun getActionButton(app: ApplicationInfo): ActionButton? {
    fun getActionButton(app: ApplicationInfo): ActionButton? {
        if (!app.isInstantApp) return null
        if (!app.isInstantApp) return null


        return clearButton()
        return clearButton()
    }
    }


    private fun clearButton() = ActionButton(
    @Composable
    private fun clearButton(): ActionButton {
        val dialogPresenter = confirmDialogPresenter()
        return ActionButton(
            text = context.getString(R.string.clear_instant_app_data),
            text = context.getString(R.string.clear_instant_app_data),
            imageVector = Icons.Outlined.Delete,
            imageVector = Icons.Outlined.Delete,
    ) { openConfirmDialog = true }
            onClick = dialogPresenter::open,
        )
    }


    @Composable
    @Composable
    fun ClearConfirmDialog() {
    private fun confirmDialogPresenter() = rememberAlertDialogPresenter(
        if (!openConfirmDialog) return
        confirmButton = AlertDialogButton(
        AlertDialog(
            text = stringResource(R.string.clear_instant_app_data),
            onDismissRequest = { openConfirmDialog = false },
            onClick = packageInfoPresenter::clearInstantApp,
            confirmButton = {
        ),
                TextButton(
        dismissButton = AlertDialogButton(stringResource(R.string.cancel)),
                    onClick = {
        title = stringResource(R.string.clear_instant_app_data),
                        openConfirmDialog = false
        text = { Text(stringResource(R.string.clear_instant_app_confirmation)) },
                        packageInfoPresenter.clearInstantApp()
                    },
                ) {
                    Text(stringResource(R.string.clear_instant_app_data))
                }
            },
            dismissButton = {
                TextButton(onClick = { openConfirmDialog = false }) {
                    Text(stringResource(R.string.cancel))
                }
            },
            title = {
                Text(stringResource(R.string.clear_instant_app_data))
            },
            text = {
                Text(stringResource(R.string.clear_instant_app_confirmation))
            },
    )
    )
}
}
}
+25 −39
Original line number Original line Diff line number Diff line
@@ -20,18 +20,15 @@ import android.content.pm.ApplicationInfo
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowCircleDown
import androidx.compose.material.icons.outlined.ArrowCircleDown
import androidx.compose.material.icons.outlined.HideSource
import androidx.compose.material.icons.outlined.HideSource
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.Utils
import com.android.settings.overlay.FeatureFactory
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed
import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed
@@ -49,8 +46,7 @@ class AppDisableButton(
    private val applicationFeatureProvider =
    private val applicationFeatureProvider =
        FeatureFactory.getFactory(context).getApplicationFeatureProvider(context)
        FeatureFactory.getFactory(context).getApplicationFeatureProvider(context)


    private var openConfirmDialog by mutableStateOf(false)
    @Composable

    fun getActionButton(app: ApplicationInfo): ActionButton? {
    fun getActionButton(app: ApplicationInfo): ActionButton? {
        if (!app.isSystemApp) return null
        if (!app.isSystemApp) return null


@@ -92,14 +88,19 @@ class AppDisableButton(
        else -> true
        else -> true
    }
    }


    private fun disableButton(app: ApplicationInfo) = ActionButton(
    @Composable
    private fun disableButton(app: ApplicationInfo): ActionButton {
        val dialogPresenter = confirmDialogPresenter()
        return ActionButton(
            text = context.getString(R.string.disable_text),
            text = context.getString(R.string.disable_text),
            imageVector = Icons.Outlined.HideSource,
            imageVector = Icons.Outlined.HideSource,
            enabled = app.canBeDisabled(),
            enabled = app.canBeDisabled(),
        ) {
        ) {
        // Currently we apply the same device policy for both the uninstallation and disable button.
            // Currently we apply the same device policy for both the uninstallation and disable
            // button.
            if (!appButtonRepository.isUninstallBlockedByAdmin(app)) {
            if (!appButtonRepository.isUninstallBlockedByAdmin(app)) {
            openConfirmDialog = true
                dialogPresenter.open()
            }
        }
        }
    }
    }


@@ -109,28 +110,13 @@ class AppDisableButton(
    ) { packageInfoPresenter.enable() }
    ) { packageInfoPresenter.enable() }


    @Composable
    @Composable
    fun DisableConfirmDialog() {
    private fun confirmDialogPresenter() = rememberAlertDialogPresenter(
        if (!openConfirmDialog) return
        confirmButton = AlertDialogButton(
        AlertDialog(
            text = stringResource(R.string.reset_app_preferences_button),
            onDismissRequest = { openConfirmDialog = false },
            onClick = packageInfoPresenter::disable,
            confirmButton = {
        ),
                TextButton(
        dismissButton = AlertDialogButton(stringResource(R.string.cancel)),
                    onClick = {
        title = stringResource(R.string.app_disable_dlg_positive),
                        openConfirmDialog = false
        text = { Text(stringResource(R.string.app_disable_dlg_text)) },
                        packageInfoPresenter.disable()
                    },
                ) {
                    Text(stringResource(R.string.app_disable_dlg_positive))
                }
            },
            dismissButton = {
                TextButton(onClick = { openConfirmDialog = false }) {
                    Text(stringResource(R.string.cancel))
                }
            },
            text = {
                Text(stringResource(R.string.app_disable_dlg_text))
            },
    )
    )
}
}
}
+20 −37
Original line number Original line Diff line number Diff line
@@ -21,19 +21,17 @@ import android.content.pm.ApplicationInfo
import android.os.UserManager
import android.os.UserManager
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.R
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.AlertDialogPresenter
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
import com.android.settingslib.spaprivileged.model.app.hasFlag
import com.android.settingslib.spaprivileged.model.app.hasFlag
import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
import com.android.settingslib.spaprivileged.model.app.userId
import com.android.settingslib.spaprivileged.model.app.userId
@@ -45,14 +43,14 @@ class AppForceStopButton(
    private val appButtonRepository = AppButtonRepository(context)
    private val appButtonRepository = AppButtonRepository(context)
    private val packageManager = context.packageManager
    private val packageManager = context.packageManager


    private var openConfirmDialog by mutableStateOf(false)
    @Composable

    fun getActionButton(app: ApplicationInfo): ActionButton {
    fun getActionButton(app: ApplicationInfo): ActionButton {
        val dialogPresenter = confirmDialogPresenter()
        return ActionButton(
        return ActionButton(
            text = context.getString(R.string.force_stop),
            text = context.getString(R.string.force_stop),
            imageVector = Icons.Outlined.WarningAmber,
            imageVector = Icons.Outlined.WarningAmber,
            enabled = isForceStopButtonEnable(app),
            enabled = isForceStopButtonEnable(app),
        ) { onForceStopButtonClicked(app) }
        ) { onForceStopButtonClicked(app, dialogPresenter) }
    }
    }


    /**
    /**
@@ -68,13 +66,16 @@ class AppForceStopButton(
        else -> !app.hasFlag(ApplicationInfo.FLAG_STOPPED)
        else -> !app.hasFlag(ApplicationInfo.FLAG_STOPPED)
    }
    }


    private fun onForceStopButtonClicked(app: ApplicationInfo) {
    private fun onForceStopButtonClicked(
        app: ApplicationInfo,
        dialogPresenter: AlertDialogPresenter,
    ) {
        packageInfoPresenter.logAction(SettingsEnums.ACTION_APP_INFO_FORCE_STOP)
        packageInfoPresenter.logAction(SettingsEnums.ACTION_APP_INFO_FORCE_STOP)
        getAdminRestriction(app)?.let { admin ->
        getAdminRestriction(app)?.let { admin ->
            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin)
            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin)
            return
            return
        }
        }
        openConfirmDialog = true
        dialogPresenter.open()
    }
    }


    private fun getAdminRestriction(app: ApplicationInfo): EnforcedAdmin? = when {
    private fun getAdminRestriction(app: ApplicationInfo): EnforcedAdmin? = when {
@@ -88,31 +89,13 @@ class AppForceStopButton(
    }
    }


    @Composable
    @Composable
    fun ForceStopConfirmDialog() {
    private fun confirmDialogPresenter() = rememberAlertDialogPresenter(
        if (!openConfirmDialog) return
        confirmButton = AlertDialogButton(
        AlertDialog(
            text = stringResource(R.string.okay),
            onDismissRequest = { openConfirmDialog = false },
            onClick = packageInfoPresenter::forceStop,
            confirmButton = {
        ),
                TextButton(
        dismissButton = AlertDialogButton(stringResource(R.string.cancel)),
                    onClick = {
        title = stringResource(R.string.force_stop_dlg_title),
                        openConfirmDialog = false
        text = { Text(stringResource(R.string.force_stop_dlg_text)) },
                        packageInfoPresenter.forceStop()
                    },
                ) {
                    Text(stringResource(R.string.okay))
                }
            },
            dismissButton = {
                TextButton(onClick = { openConfirmDialog = false }) {
                    Text(stringResource(R.string.cancel))
                }
            },
            title = {
                Text(stringResource(R.string.force_stop_dlg_title))
            },
            text = {
                Text(stringResource(R.string.force_stop_dlg_text))
            },
    )
    )
}
}
}
+19 −6
Original line number Original line Diff line number Diff line
@@ -21,16 +21,19 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager
import android.os.UserManager
import android.os.UserManager
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.Utils
import com.android.settings.Utils
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.After
import org.junit.Before
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mock
@@ -42,6 +45,8 @@ import org.mockito.Mockito.`when` as whenever


@RunWith(AndroidJUnit4::class)
@RunWith(AndroidJUnit4::class)
class AppDisableButtonTest {
class AppDisableButtonTest {
    @get:Rule
    val composeTestRule = createComposeRule()


    private lateinit var mockSession: MockitoSession
    private lateinit var mockSession: MockitoSession


@@ -97,7 +102,7 @@ class AppDisableButtonTest {
            privateFlags = privateFlags or ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
            privateFlags = privateFlags or ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
        }
        }


        val actionButton = appDisableButton.getActionButton(app)!!
        val actionButton = setDisableButton(app)


        assertThat(actionButton.enabled).isFalse()
        assertThat(actionButton.enabled).isFalse()
    }
    }
@@ -108,7 +113,7 @@ class AppDisableButtonTest {
            privateFlags = privateFlags or ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY
            privateFlags = privateFlags or ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY
        }
        }


        val actionButton = appDisableButton.getActionButton(app)!!
        val actionButton = setDisableButton(app)


        assertThat(actionButton.enabled).isFalse()
        assertThat(actionButton.enabled).isFalse()
    }
    }
@@ -118,7 +123,7 @@ class AppDisableButtonTest {
        whenever(appFeatureProvider.keepEnabledPackages).thenReturn(setOf(PACKAGE_NAME))
        whenever(appFeatureProvider.keepEnabledPackages).thenReturn(setOf(PACKAGE_NAME))
        val app = enabledSystemApp()
        val app = enabledSystemApp()


        val actionButton = appDisableButton.getActionButton(app)!!
        val actionButton = setDisableButton(app)


        assertThat(actionButton.enabled).isFalse()
        assertThat(actionButton.enabled).isFalse()
    }
    }
@@ -130,7 +135,7 @@ class AppDisableButtonTest {
        ).thenReturn(true)
        ).thenReturn(true)
        val app = enabledSystemApp()
        val app = enabledSystemApp()


        val actionButton = appDisableButton.getActionButton(app)!!
        val actionButton = setDisableButton(app)


        assertThat(actionButton.enabled).isFalse()
        assertThat(actionButton.enabled).isFalse()
    }
    }
@@ -141,7 +146,7 @@ class AppDisableButtonTest {
            .thenReturn(true)
            .thenReturn(true)
        val app = enabledSystemApp()
        val app = enabledSystemApp()


        val actionButton = appDisableButton.getActionButton(app)!!
        val actionButton = setDisableButton(app)


        assertThat(actionButton.enabled).isFalse()
        assertThat(actionButton.enabled).isFalse()
    }
    }
@@ -150,11 +155,19 @@ class AppDisableButtonTest {
    fun getActionButton_regularEnabledSystemApp_canDisable() {
    fun getActionButton_regularEnabledSystemApp_canDisable() {
        val app = enabledSystemApp()
        val app = enabledSystemApp()


        val actionButton = appDisableButton.getActionButton(app)!!
        val actionButton = setDisableButton(app)


        assertThat(actionButton.enabled).isTrue()
        assertThat(actionButton.enabled).isTrue()
    }
    }


    private fun setDisableButton(app: ApplicationInfo): ActionButton {
        lateinit var actionButton: ActionButton
        composeTestRule.setContent {
            actionButton = appDisableButton.getActionButton(app)!!
        }
        return actionButton
    }

    private fun enabledSystemApp(builder: ApplicationInfo.() -> Unit = {}) =
    private fun enabledSystemApp(builder: ApplicationInfo.() -> Unit = {}) =
        ApplicationInfo().apply {
        ApplicationInfo().apply {
            packageName = PACKAGE_NAME
            packageName = PACKAGE_NAME
Loading