• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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