Loading src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt +12 −18 Original line number Diff line number Diff line Loading @@ -17,19 +17,18 @@ package com.android.settings.spa.app.appsettings import android.app.ActivityManager import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.os.UserManager import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.Utils import com.android.settingslib.spaprivileged.model.app.userId import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.model.app.isDisallowControl class AppButtonRepository(private val context: Context) { private val packageManager = context.packageManager private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! private val devicePolicyManager = context.devicePolicyManager /** * Checks whether the given application is disallowed from modifying. Loading @@ -41,20 +40,10 @@ class AppButtonRepository(private val context: Context) { // If the uninstallation intent is already queued, disable the button. devicePolicyManager.isUninstallInQueue(app.packageName) -> true RestrictedLockUtilsInternal.hasBaseUserRestriction( context, UserManager.DISALLOW_APPS_CONTROL, app.userId ) -> true else -> false else -> app.isDisallowControl(context) } /** * Checks whether the given application is an active admin. */ fun isActiveAdmin(app: ApplicationInfo): Boolean = devicePolicyManager.packageHasActiveAdmins(app.packageName, app.userId) fun getHomePackageInfo(): AppUninstallButton.HomePackages { fun getHomePackageInfo(): HomePackages { val homePackages = mutableSetOf<String>() val homeActivities = ArrayList<ResolveInfo>() val currentDefaultHome = packageManager.getHomeActivities(homeActivities) Loading @@ -66,7 +55,7 @@ class AppButtonRepository(private val context: Context) { homePackages.add(metaPackageName) } } return AppUninstallButton.HomePackages(homePackages, currentDefaultHome) return HomePackages(homePackages, currentDefaultHome) } private fun signaturesMatch(packageName1: String, packageName2: String): Boolean = try { Loading @@ -75,4 +64,9 @@ class AppButtonRepository(private val context: Context) { // e.g. named alternate package not found during lookup; this is an expected case sometimes false } data class HomePackages( val homePackages: Set<String>, val currentDefaultHome: ComponentName?, ) } src/com/android/settings/spa/app/appsettings/AppDisableButton.kt +7 −8 Original line number Diff line number Diff line Loading @@ -16,10 +16,7 @@ package com.android.settings.spa.app.appsettings import android.app.admin.DevicePolicyManager import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.os.UserManager import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowCircleDown import androidx.compose.material.icons.outlined.HideSource Loading @@ -34,10 +31,12 @@ 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.Utils as SettingsLibUtils import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.model.app.hasFlag import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed import com.android.settingslib.Utils as SettingsLibUtils class AppDisableButton( private val packageInfoPresenter: PackageInfoPresenter, Loading @@ -46,8 +45,8 @@ class AppDisableButton( private val appButtonRepository = AppButtonRepository(context) private val resources = context.resources private val packageManager = context.packageManager private val userManager = context.getSystemService(UserManager::class.java)!! private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! private val userManager = context.userManager private val devicePolicyManager = context.devicePolicyManager private val applicationFeatureProvider = FeatureFactory.getFactory(context).getApplicationFeatureProvider(context) Loading Loading @@ -84,7 +83,7 @@ class AppDisableButton( SettingsLibUtils.isSystemPackage(resources, packageManager, packageInfo) -> false // If this is a device admin, it can't be disabled. appButtonRepository.isActiveAdmin(app) -> false app.isActiveAdmin(context) -> false // We don't allow disabling DO/PO on *any* users if it's a system app, because // "disabling" is actually "downgrade to the system version + disable", and "downgrade" Loading src/com/android/settings/spa/app/appsettings/AppForceStopButton.kt +2 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.model.app.hasFlag import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.userId class AppForceStopButton( Loading @@ -61,7 +62,7 @@ class AppForceStopButton( */ private fun isForceStopButtonEnable(app: ApplicationInfo): Boolean = when { // User can't force stop device admin. appButtonRepository.isActiveAdmin(app) -> false app.isActiveAdmin(context) -> false appButtonRepository.isDisallowControl(app) -> false Loading src/com/android/settings/spa/app/appsettings/AppSettings.kt +8 −3 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ object AppSettingsProvider : SettingsPageProvider { PackageInfoPresenter(context, packageName, userId, coroutineScope) } AppSettings(packageInfoPresenter) packageInfoPresenter.PageCloser() packageInfoPresenter.PackageRemoveDetector() } @Composable Loading @@ -77,7 +77,13 @@ object AppSettingsProvider : SettingsPageProvider { @Composable private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) { val packageInfo = packageInfoPresenter.flow.collectAsState().value ?: return RegularScaffold(title = stringResource(R.string.application_info_label)) { val app = packageInfo.applicationInfo RegularScaffold( title = stringResource(R.string.application_info_label), actions = { AppSettingsMoreOptions(packageInfoPresenter, app) } ) { val appInfoProvider = remember { AppInfoProvider(packageInfo) } appInfoProvider.AppInfo() Loading @@ -85,7 +91,6 @@ private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) { AppButtons(packageInfoPresenter) Category(title = stringResource(R.string.advanced_apps)) { val app = packageInfo.applicationInfo DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app) ModifySystemSettingsAppListProvider.InfoPageEntryItem(app) PictureInPictureListProvider.InfoPageEntryItem(app) Loading src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa.app.appsettings import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settings.Utils import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.isDisallowControl import com.android.settingslib.spaprivileged.model.app.userId @Composable fun AppSettingsMoreOptions(packageInfoPresenter: PackageInfoPresenter, app: ApplicationInfo) { val context = LocalContext.current // We don't allow uninstalling update for DO/PO if it's a system app, because it will clear data // on all users. We also don't allow uninstalling for all users if it's DO/PO for any user. val isProfileOrDeviceOwner = remember(app) { Utils.isProfileOrDeviceOwner( context.userManager, context.devicePolicyManager, app.packageName ) } if (isProfileOrDeviceOwner) return val shownUninstallUpdates = remember(app) { isShowUninstallUpdates(context, app) } val shownUninstallForAllUsers = remember(app) { isShowUninstallForAllUsers(context, app) } if (!shownUninstallUpdates && !shownUninstallForAllUsers) return MoreOptionsAction { onDismissRequest -> if (shownUninstallUpdates) { DropdownMenuItem( text = { Text(stringResource(R.string.app_factory_reset)) }, onClick = { onDismissRequest() packageInfoPresenter.startUninstallActivity(forAllUsers = false) }, ) } if (shownUninstallForAllUsers) { DropdownMenuItem( text = { Text(stringResource(R.string.uninstall_all_users_text)) }, onClick = { onDismissRequest() packageInfoPresenter.startUninstallActivity(forAllUsers = true) }, ) } } } private fun isShowUninstallUpdates(context: Context, app: ApplicationInfo): Boolean = app.isUpdatedSystemApp && context.userManager.isUserAdmin(app.userId) && !app.isDisallowControl(context) && !context.resources.getBoolean(R.bool.config_disable_uninstall_update) private fun isShowUninstallForAllUsers(context: Context, app: ApplicationInfo): Boolean = app.userId == 0 && !app.isSystemApp && !app.isInstantApp && !app.isActiveAdmin(context) && isOtherUserHasInstallPackage(context, app) private fun isOtherUserHasInstallPackage(context: Context, app: ApplicationInfo): Boolean = context.userManager.aliveUsers .filter { it.id != app.userId } .any { PackageManagers.isPackageInstalledAsUser(app.packageName, it.id) } Loading
src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt +12 −18 Original line number Diff line number Diff line Loading @@ -17,19 +17,18 @@ package com.android.settings.spa.app.appsettings import android.app.ActivityManager import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.os.UserManager import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.Utils import com.android.settingslib.spaprivileged.model.app.userId import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.model.app.isDisallowControl class AppButtonRepository(private val context: Context) { private val packageManager = context.packageManager private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! private val devicePolicyManager = context.devicePolicyManager /** * Checks whether the given application is disallowed from modifying. Loading @@ -41,20 +40,10 @@ class AppButtonRepository(private val context: Context) { // If the uninstallation intent is already queued, disable the button. devicePolicyManager.isUninstallInQueue(app.packageName) -> true RestrictedLockUtilsInternal.hasBaseUserRestriction( context, UserManager.DISALLOW_APPS_CONTROL, app.userId ) -> true else -> false else -> app.isDisallowControl(context) } /** * Checks whether the given application is an active admin. */ fun isActiveAdmin(app: ApplicationInfo): Boolean = devicePolicyManager.packageHasActiveAdmins(app.packageName, app.userId) fun getHomePackageInfo(): AppUninstallButton.HomePackages { fun getHomePackageInfo(): HomePackages { val homePackages = mutableSetOf<String>() val homeActivities = ArrayList<ResolveInfo>() val currentDefaultHome = packageManager.getHomeActivities(homeActivities) Loading @@ -66,7 +55,7 @@ class AppButtonRepository(private val context: Context) { homePackages.add(metaPackageName) } } return AppUninstallButton.HomePackages(homePackages, currentDefaultHome) return HomePackages(homePackages, currentDefaultHome) } private fun signaturesMatch(packageName1: String, packageName2: String): Boolean = try { Loading @@ -75,4 +64,9 @@ class AppButtonRepository(private val context: Context) { // e.g. named alternate package not found during lookup; this is an expected case sometimes false } data class HomePackages( val homePackages: Set<String>, val currentDefaultHome: ComponentName?, ) }
src/com/android/settings/spa/app/appsettings/AppDisableButton.kt +7 −8 Original line number Diff line number Diff line Loading @@ -16,10 +16,7 @@ package com.android.settings.spa.app.appsettings import android.app.admin.DevicePolicyManager import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.os.UserManager import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowCircleDown import androidx.compose.material.icons.outlined.HideSource Loading @@ -34,10 +31,12 @@ 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.Utils as SettingsLibUtils import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.model.app.hasFlag import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed import com.android.settingslib.Utils as SettingsLibUtils class AppDisableButton( private val packageInfoPresenter: PackageInfoPresenter, Loading @@ -46,8 +45,8 @@ class AppDisableButton( private val appButtonRepository = AppButtonRepository(context) private val resources = context.resources private val packageManager = context.packageManager private val userManager = context.getSystemService(UserManager::class.java)!! private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! private val userManager = context.userManager private val devicePolicyManager = context.devicePolicyManager private val applicationFeatureProvider = FeatureFactory.getFactory(context).getApplicationFeatureProvider(context) Loading Loading @@ -84,7 +83,7 @@ class AppDisableButton( SettingsLibUtils.isSystemPackage(resources, packageManager, packageInfo) -> false // If this is a device admin, it can't be disabled. appButtonRepository.isActiveAdmin(app) -> false app.isActiveAdmin(context) -> false // We don't allow disabling DO/PO on *any* users if it's a system app, because // "disabling" is actually "downgrade to the system version + disable", and "downgrade" Loading
src/com/android/settings/spa/app/appsettings/AppForceStopButton.kt +2 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.model.app.hasFlag import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.userId class AppForceStopButton( Loading @@ -61,7 +62,7 @@ class AppForceStopButton( */ private fun isForceStopButtonEnable(app: ApplicationInfo): Boolean = when { // User can't force stop device admin. appButtonRepository.isActiveAdmin(app) -> false app.isActiveAdmin(context) -> false appButtonRepository.isDisallowControl(app) -> false Loading
src/com/android/settings/spa/app/appsettings/AppSettings.kt +8 −3 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ object AppSettingsProvider : SettingsPageProvider { PackageInfoPresenter(context, packageName, userId, coroutineScope) } AppSettings(packageInfoPresenter) packageInfoPresenter.PageCloser() packageInfoPresenter.PackageRemoveDetector() } @Composable Loading @@ -77,7 +77,13 @@ object AppSettingsProvider : SettingsPageProvider { @Composable private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) { val packageInfo = packageInfoPresenter.flow.collectAsState().value ?: return RegularScaffold(title = stringResource(R.string.application_info_label)) { val app = packageInfo.applicationInfo RegularScaffold( title = stringResource(R.string.application_info_label), actions = { AppSettingsMoreOptions(packageInfoPresenter, app) } ) { val appInfoProvider = remember { AppInfoProvider(packageInfo) } appInfoProvider.AppInfo() Loading @@ -85,7 +91,6 @@ private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) { AppButtons(packageInfoPresenter) Category(title = stringResource(R.string.advanced_apps)) { val app = packageInfo.applicationInfo DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app) ModifySystemSettingsAppListProvider.InfoPageEntryItem(app) PictureInPictureListProvider.InfoPageEntryItem(app) Loading
src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa.app.appsettings import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settings.Utils import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.isDisallowControl import com.android.settingslib.spaprivileged.model.app.userId @Composable fun AppSettingsMoreOptions(packageInfoPresenter: PackageInfoPresenter, app: ApplicationInfo) { val context = LocalContext.current // We don't allow uninstalling update for DO/PO if it's a system app, because it will clear data // on all users. We also don't allow uninstalling for all users if it's DO/PO for any user. val isProfileOrDeviceOwner = remember(app) { Utils.isProfileOrDeviceOwner( context.userManager, context.devicePolicyManager, app.packageName ) } if (isProfileOrDeviceOwner) return val shownUninstallUpdates = remember(app) { isShowUninstallUpdates(context, app) } val shownUninstallForAllUsers = remember(app) { isShowUninstallForAllUsers(context, app) } if (!shownUninstallUpdates && !shownUninstallForAllUsers) return MoreOptionsAction { onDismissRequest -> if (shownUninstallUpdates) { DropdownMenuItem( text = { Text(stringResource(R.string.app_factory_reset)) }, onClick = { onDismissRequest() packageInfoPresenter.startUninstallActivity(forAllUsers = false) }, ) } if (shownUninstallForAllUsers) { DropdownMenuItem( text = { Text(stringResource(R.string.uninstall_all_users_text)) }, onClick = { onDismissRequest() packageInfoPresenter.startUninstallActivity(forAllUsers = true) }, ) } } } private fun isShowUninstallUpdates(context: Context, app: ApplicationInfo): Boolean = app.isUpdatedSystemApp && context.userManager.isUserAdmin(app.userId) && !app.isDisallowControl(context) && !context.resources.getBoolean(R.bool.config_disable_uninstall_update) private fun isShowUninstallForAllUsers(context: Context, app: ApplicationInfo): Boolean = app.userId == 0 && !app.isSystemApp && !app.isInstantApp && !app.isActiveAdmin(context) && isOtherUserHasInstallPackage(context, app) private fun isOtherUserHasInstallPackage(context: Context, app: ApplicationInfo): Boolean = context.userManager.aliveUsers .filter { it.id != app.userId } .any { PackageManagers.isPackageInstalledAsUser(app.packageName, it.id) }