1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.spa.app.appinfo 18 19 import android.content.pm.ApplicationInfo 20 import androidx.compose.material.icons.Icons 21 import androidx.compose.material.icons.outlined.ArrowCircleDown 22 import androidx.compose.material.icons.outlined.HideSource 23 import androidx.compose.material3.Text 24 import androidx.compose.runtime.Composable 25 import androidx.compose.ui.res.stringResource 26 import com.android.settings.R 27 import com.android.settings.Utils 28 import com.android.settings.overlay.FeatureFactory 29 import com.android.settingslib.spa.widget.button.ActionButton 30 import com.android.settingslib.spa.widget.dialog.AlertDialogButton 31 import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter 32 import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager 33 import com.android.settingslib.spaprivileged.framework.common.userManager 34 import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed 35 import com.android.settingslib.Utils as SettingsLibUtils 36 37 class AppDisableButton( 38 private val packageInfoPresenter: PackageInfoPresenter, 39 ) { 40 private val context = packageInfoPresenter.context 41 private val appButtonRepository = AppButtonRepository(context) 42 private val resources = context.resources 43 private val packageManager = context.packageManager 44 private val userManager = context.userManager 45 private val devicePolicyManager = context.devicePolicyManager 46 private val applicationFeatureProvider = 47 FeatureFactory.getFactory(context).getApplicationFeatureProvider(context) 48 49 @Composable getActionButtonnull50 fun getActionButton(app: ApplicationInfo): ActionButton? { 51 if (!app.isSystemApp) return null 52 53 return when { 54 app.enabled && !app.isDisabledUntilUsed -> { 55 disableButton(app) 56 } 57 58 else -> enableButton() 59 } 60 } 61 62 /** 63 * Gets whether a package can be disabled. 64 */ canBeDisablednull65 private fun ApplicationInfo.canBeDisabled(): Boolean = when { 66 // Try to prevent the user from bricking their phone by not allowing disabling of apps 67 // signed with the system certificate. 68 isSignedWithPlatformKey -> false 69 70 // system/vendor resource overlays can never be disabled. 71 isResourceOverlay -> false 72 73 packageName in applicationFeatureProvider.keepEnabledPackages -> false 74 75 // Home launcher apps need special handling. In system ones we don't risk downgrading 76 // because that can interfere with home-key resolution. 77 packageName in appButtonRepository.getHomePackageInfo().homePackages -> false 78 79 SettingsLibUtils.isEssentialPackage(resources, packageManager, packageName) -> false 80 81 // We don't allow disabling DO/PO on *any* users if it's a system app, because 82 // "disabling" is actually "downgrade to the system version + disable", and "downgrade" 83 // will clear data on all users. 84 Utils.isProfileOrDeviceOwner(userManager, devicePolicyManager, packageName) -> false 85 86 appButtonRepository.isDisallowControl(this) -> false 87 88 else -> true 89 } 90 91 @Composable disableButtonnull92 private fun disableButton(app: ApplicationInfo): ActionButton { 93 val dialogPresenter = confirmDialogPresenter() 94 return ActionButton( 95 text = context.getString(R.string.disable_text), 96 imageVector = Icons.Outlined.HideSource, 97 enabled = app.canBeDisabled(), 98 ) { 99 // Currently we apply the same device policy for both the uninstallation and disable 100 // button. 101 if (!appButtonRepository.isUninstallBlockedByAdmin(app)) { 102 dialogPresenter.open() 103 } 104 } 105 } 106 enableButtonnull107 private fun enableButton() = ActionButton( 108 text = context.getString(R.string.enable_text), 109 imageVector = Icons.Outlined.ArrowCircleDown, 110 ) { packageInfoPresenter.enable() } 111 112 @Composable confirmDialogPresenternull113 private fun confirmDialogPresenter() = rememberAlertDialogPresenter( 114 confirmButton = AlertDialogButton( 115 text = stringResource(R.string.app_disable_dlg_positive), 116 onClick = packageInfoPresenter::disable, 117 ), 118 dismissButton = AlertDialogButton(stringResource(R.string.cancel)), 119 text = { Text(stringResource(R.string.app_disable_dlg_text)) }, 120 ) 121 } 122