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

Commit a72e62b3 authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Use SettingsAlertDialog for app button dialogs"

parents a5cdbee7 750c6072
Loading
Loading
Loading
Loading
+6 −15
Original line number Diff line number Diff line
@@ -24,14 +24,12 @@ 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.map

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

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

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

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

    @Composable
    fun Dialogs() {
        appDisableButton.DisableConfirmDialog()
        appClearButton.ClearConfirmDialog()
        appForceStopButton.ForceStopConfirmDialog()
    }
}
+20 −37
Original line number Diff line number Diff line
@@ -19,61 +19,44 @@ package com.android.settings.spa.app.appinfo
import android.content.pm.ApplicationInfo
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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 com.android.settings.R
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(
    private val packageInfoPresenter: PackageInfoPresenter,
) {
    private val context = packageInfoPresenter.context

    private var openConfirmDialog by mutableStateOf(false)

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

        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),
            imageVector = Icons.Outlined.Delete,
    ) { openConfirmDialog = true }
            onClick = dialogPresenter::open,
        )
    }

    @Composable
    fun ClearConfirmDialog() {
        if (!openConfirmDialog) return
        AlertDialog(
            onDismissRequest = { openConfirmDialog = false },
            confirmButton = {
                TextButton(
                    onClick = {
                        openConfirmDialog = false
                        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))
            },
    private fun confirmDialogPresenter() = rememberAlertDialogPresenter(
        confirmButton = AlertDialogButton(
            text = stringResource(R.string.clear_instant_app_data),
            onClick = packageInfoPresenter::clearInstantApp,
        ),
        dismissButton = AlertDialogButton(stringResource(R.string.cancel)),
        title = stringResource(R.string.clear_instant_app_data),
        text = { Text(stringResource(R.string.clear_instant_app_confirmation)) },
    )
}
}
+25 −39
Original line number 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.outlined.ArrowCircleDown
import androidx.compose.material.icons.outlined.HideSource
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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 com.android.settings.R
import com.android.settings.Utils
import com.android.settings.overlay.FeatureFactory
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.userManager
import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed
@@ -49,8 +46,7 @@ class AppDisableButton(
    private val applicationFeatureProvider =
        FeatureFactory.getFactory(context).getApplicationFeatureProvider(context)

    private var openConfirmDialog by mutableStateOf(false)

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

@@ -92,14 +88,19 @@ class AppDisableButton(
        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),
            imageVector = Icons.Outlined.HideSource,
            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)) {
            openConfirmDialog = true
                dialogPresenter.open()
            }
        }
    }

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

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

    private var openConfirmDialog by mutableStateOf(false)

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

    /**
@@ -68,13 +66,16 @@ class AppForceStopButton(
        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)
        getAdminRestriction(app)?.let { admin ->
            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin)
            return
        }
        openConfirmDialog = true
        dialogPresenter.open()
    }

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

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

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

    private lateinit var mockSession: MockitoSession

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

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

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

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

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

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

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

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

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

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

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

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

        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 = {}) =
        ApplicationInfo().apply {
            packageName = PACKAGE_NAME
Loading