diff --git a/src/com/android/settings/spa/app/AppUtil.kt b/src/com/android/settings/spa/app/AppUtil.kt new file mode 100644 index 00000000000..64da61380f5 --- /dev/null +++ b/src/com/android/settings/spa/app/AppUtil.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 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 + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.UserHandle + +/** + * Based on PackageManagerService design, and it looks like the suggested replacement in the + * deprecate notes suggest that we use PackageInstaller.uninstall which does not guarantee a pop up + * would open and depends on the calling application. Seems like further investigation is needed + * before we can move over to the new API. + */ +@Suppress("DEPRECATION") +fun Context.startUninstallActivity( + packageName: String, + userHandle: UserHandle, + forAllUsers: Boolean = false, +) { + val packageUri = Uri.parse("package:$packageName") + + val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply { + putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, forAllUsers) + } + startActivityAsUser(intent, userHandle) +} \ No newline at end of file diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt index cb74388278e..51426a1bdfe 100644 --- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt +++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt @@ -22,11 +22,11 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageInfo import android.content.pm.PackageManager -import android.net.Uri import android.os.UserHandle import android.util.Log import androidx.compose.runtime.Composable import com.android.settings.overlay.FeatureFactory +import com.android.settings.spa.app.startUninstallActivity import com.android.settingslib.spa.framework.compose.LocalNavController import com.android.settingslib.spaprivileged.framework.common.activityManager import com.android.settingslib.spaprivileged.framework.common.asUser @@ -116,11 +116,7 @@ class PackageInfoPresenter( /** Starts the uninstallation activity. */ fun startUninstallActivity(forAllUsers: Boolean = false) { logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP) - val packageUri = Uri.parse("package:${packageName}") - val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply { - putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, forAllUsers) - } - context.startActivityAsUser(intent, userHandle) + context.startUninstallActivity(packageName, userHandle, forAllUsers) } /** Clears this instant app. */ diff --git a/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt b/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt index 9cf95169d42..a6b10dd4fba 100644 --- a/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt +++ b/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt @@ -17,13 +17,11 @@ package com.android.settings.spa.app.backgroundinstall import android.content.Context -import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.IBackgroundInstallControlService import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.ParceledListSlice -import android.net.Uri import android.os.Bundle import android.os.ServiceManager import android.provider.DeviceConfig @@ -40,6 +38,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider +import com.android.settings.spa.app.startUninstallActivity import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SettingsPageProvider @@ -54,6 +53,7 @@ import com.android.settingslib.spa.widget.ui.SettingsBody import com.android.settingslib.spaprivileged.model.app.AppEntry import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppRecord +import com.android.settingslib.spaprivileged.model.app.userHandle import com.android.settingslib.spaprivileged.template.app.AppList import com.android.settingslib.spaprivileged.template.app.AppListButtonItem import com.android.settingslib.spaprivileged.template.app.AppListInput @@ -150,23 +150,6 @@ fun BackgroundInstalledAppList( } ) } -/* -Based on PackageManagerService design, and it looks like the suggested replacement in the deprecate -notes suggest that we use PackageInstaller.uninstall which does not guarantee a pop up would open -and depends on the calling application. Seems like further investigation is needed before we can -move over to the new API. - */ -@Suppress -@VisibleForTesting -fun startUninstallActivity(context: Context, - packageName: String, - forAllUsers: Boolean = false) { - val packageUri = Uri.parse("package:${packageName}") - val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply { - putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, forAllUsers) - } - context.startActivityAsUser(intent, context.user) -} data class BackgroundInstalledAppListWithGroupingAppRecord( override val app: ApplicationInfo, @@ -190,9 +173,10 @@ class BackgroundInstalledAppsWithGroupingListModel(private val context: Context) @Composable override fun AppListItemModel.AppItem() { val context = LocalContext.current + val app = record.app AppListButtonItem( - onClick = AppInfoSettingsProvider.navigator(app = record.app), - onButtonClick = { startUninstallActivity(context, record.app.packageName) }, + onClick = AppInfoSettingsProvider.navigator(app = app), + onButtonClick = { context.startUninstallActivity(app.packageName, app.userHandle) }, buttonIcon = Icons.Outlined.Delete, buttonIconDescription = stringResource( R.string.background_install_uninstall_button_description)) @@ -209,11 +193,6 @@ class BackgroundInstalledAppsWithGroupingListModel(private val context: Context) } } - @Composable - override fun getSummary(option: Int, record: BackgroundInstalledAppListWithGroupingAppRecord) - = null - - @Suppress override fun filter( userIdFlow: Flow, option: Int, @@ -224,6 +203,7 @@ class BackgroundInstalledAppsWithGroupingListModel(private val context: Context) return flowOf() } return userIdFlow.combine(recordListFlow) { userId, recordList -> + @Suppress("UNCHECKED_CAST") val appList = (backgroundInstallService.getBackgroundInstalledPackages( PackageManager.MATCH_ALL.toLong(), userId) as ParceledListSlice).list val appNameList = appList.map { it.packageName } diff --git a/tests/spa_unit/src/com/android/settings/spa/app/AppUtilTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/AppUtilTest.kt new file mode 100644 index 00000000000..15f87edea31 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/spa/app/AppUtilTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 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 + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Rule + +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@RunWith(AndroidJUnit4::class) +class AppUtilTest { + @get:Rule + val mockito: MockitoRule = MockitoJUnit.rule() + + @Mock + private lateinit var context: Context + + @Test + fun startUninstallActivity() { + context.startUninstallActivity(packageName = PACKAGE_NAME, userHandle = USER_HANDLE) + + val intentCaptor = ArgumentCaptor.forClass(Intent::class.java) + verify(context).startActivityAsUser(intentCaptor.capture(), eq(USER_HANDLE)) + val intent = intentCaptor.value + assertThat(intent.action).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE) + assertThat(intent.data).isEqualTo(Uri.parse("package:$PACKAGE_NAME")) + assertThat(intent.extras?.getBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS)).isFalse() + } + + private companion object { + const val PACKAGE_NAME = "package.name" + val USER_HANDLE: UserHandle = UserHandle.of(0) + } +} \ No newline at end of file diff --git a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt index 8e1757f5d1e..b3e29af3e9e 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt @@ -17,14 +17,11 @@ package com.android.settings.spa.app.backgroundinstall import android.content.Context -import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.IBackgroundInstallControlService import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.ParceledListSlice -import android.net.Uri -import android.os.UserHandle import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText @@ -74,9 +71,6 @@ class BackgroundInstalledAppsPageProviderTest { private var packageInfoFlagsCaptor = ArgumentCaptor.forClass(PackageManager.PackageInfoFlags::class.java) - private var intentCaptor = - ArgumentCaptor.forClass(Intent::class.java) - private val fakeNavControllerWrapper = FakeNavControllerWrapper() @Before @@ -177,26 +171,6 @@ class BackgroundInstalledAppsPageProviderTest { .isEqualTo("AppInfoSettings/package.name/0") } - @Suppress - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun startUninstallActivity_success() = runTest { - val expectedPackageUri = Uri.parse("package:package.name") - val mockUserHandle = UserHandle(0) - Mockito.`when`(mockContext.user).thenReturn(mockUserHandle) - Mockito.`when`(mockContext.startActivityAsUser( - intentCaptor.capture(), - eq(mockUserHandle) - )).then { } - - startUninstallActivity(mockContext, TEST_PACKAGE_NAME) - - Truth.assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE) - Truth.assertThat(intentCaptor.value.data).isEqualTo(expectedPackageUri) - Truth.assertThat(intentCaptor.value.extras?.getBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS)) - .isEqualTo(false) - } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun backgroundInstalledAppsWithGroupingListModel_getGroupTitleOne() = runTest {