Loading src/com/android/settings/spa/app/AppUtil.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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 src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt +2 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. */ Loading src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt +6 −26 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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, Loading @@ -190,9 +173,10 @@ class BackgroundInstalledAppsWithGroupingListModel(private val context: Context) @Composable override fun AppListItemModel<BackgroundInstalledAppListWithGroupingAppRecord>.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)) Loading @@ -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<Int>, option: Int, Loading @@ -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<PackageInfo>).list val appNameList = appList.map { it.packageName } Loading tests/spa_unit/src/com/android/settings/spa/app/AppUtilTest.kt 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * 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 tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt +0 −26 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 { Loading Loading
src/com/android/settings/spa/app/AppUtil.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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
src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt +2 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. */ Loading
src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt +6 −26 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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, Loading @@ -190,9 +173,10 @@ class BackgroundInstalledAppsWithGroupingListModel(private val context: Context) @Composable override fun AppListItemModel<BackgroundInstalledAppListWithGroupingAppRecord>.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)) Loading @@ -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<Int>, option: Int, Loading @@ -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<PackageInfo>).list val appNameList = appList.map { it.packageName } Loading
tests/spa_unit/src/com/android/settings/spa/app/AppUtilTest.kt 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * 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
tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt +0 −26 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 { Loading