Display App installed in other user in All Apps

Only for admin user.

Also clean up unused getInstallationStatus().

Fix: 277299765
Test: Manually with All Apps when multiple users is on
Test: Unit test
Change-Id: I4de681c101a605e3517dcd8765bf7a95d1b76417
This commit is contained in:
Chaohui Wang
2023-04-10 01:32:35 +08:00
parent bc1ee1264b
commit c688f93ed9
9 changed files with 58 additions and 46 deletions

View File

@@ -986,17 +986,6 @@ public final class Utils extends com.android.settingslib.Utils {
return false; return false;
} }
/**
* Return the resource id to represent the install status for an app
*/
@StringRes
public static int getInstallationStatus(ApplicationInfo info) {
if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
return R.string.not_installed;
}
return info.enabled ? R.string.installed : R.string.disabled;
}
private static boolean isVolumeValid(VolumeInfo volume) { private static boolean isVolumeValid(VolumeInfo volume) {
return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE) return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
&& volume.isMountedReadable(); && volume.isMountedReadable();

View File

@@ -38,6 +38,7 @@ import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.ui.SpinnerOption import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.installed
import com.android.settingslib.spaprivileged.template.app.AppList import com.android.settingslib.spaprivileged.template.app.AppList
import com.android.settingslib.spaprivileged.template.app.AppListInput import com.android.settingslib.spaprivileged.template.app.AppListInput
import com.android.settingslib.spaprivileged.template.app.AppListItem import com.android.settingslib.spaprivileged.template.app.AppListItem
@@ -75,6 +76,7 @@ fun AllAppListPage(
title = stringResource(R.string.all_apps), title = stringResource(R.string.all_apps),
listModel = rememberContext(::AllAppListModel), listModel = rememberContext(::AllAppListModel),
showInstantApps = true, showInstantApps = true,
matchAnyUserForAdmin = true,
moreOptions = { ResetAppPreferences(resetAppDialogPresenter::open) }, moreOptions = { ResetAppPreferences(resetAppDialogPresenter::open) },
appList = appList, appList = appList,
) )
@@ -133,8 +135,13 @@ class AllAppListModel(
return remember { return remember {
derivedStateOf { derivedStateOf {
storageSummary.value + storageSummary.value +
when (isDisabled(record)) { when {
true -> System.lineSeparator() + context.getString(R.string.disabled) !record.app.installed -> {
System.lineSeparator() + context.getString(R.string.not_installed)
}
isDisabled(record) -> {
System.lineSeparator() + context.getString(R.string.disabled)
}
else -> "" else -> ""
} }
} }

View File

@@ -30,8 +30,10 @@ import com.android.settings.notification.app.AppNotificationSettings
import com.android.settings.spa.notification.AppNotificationRepository import com.android.settings.spa.notification.AppNotificationRepository
import com.android.settings.spa.notification.IAppNotificationRepository import com.android.settings.spa.notification.IAppNotificationRepository
import com.android.settingslib.spa.framework.compose.rememberContext import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.installed
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
@@ -53,6 +55,7 @@ fun AppNotificationPreference(
override val summary = summaryFlow.collectAsStateWithLifecycle( override val summary = summaryFlow.collectAsStateWithLifecycle(
initialValue = stringResource(R.string.summary_placeholder) initialValue = stringResource(R.string.summary_placeholder)
) )
override val enabled = stateOf(app.installed)
override val onClick = { navigateToAppNotificationSettings(context, app) } override val onClick = { navigateToAppNotificationSettings(context, app) }
}) })
} }

View File

@@ -141,7 +141,9 @@ class PackageInfoPresenter(
private fun getPackageInfo() = private fun getPackageInfo() =
packageManagers.getPackageInfoAsUser( packageManagers.getPackageInfoAsUser(
packageName = packageName, packageName = packageName,
flags = PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.GET_PERMISSIONS, flags = PackageManager.MATCH_ANY_USER or
PackageManager.MATCH_DISABLED_COMPONENTS or
PackageManager.GET_PERMISSIONS,
userId = userId, userId = userId,
) )
} }

View File

@@ -28,6 +28,7 @@ import androidx.compose.runtime.livedata.observeAsState
import com.android.settings.R import com.android.settings.R
import com.android.settingslib.spaprivileged.model.app.AppOpsController import com.android.settingslib.spaprivileged.model.app.AppOpsController
import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.installed
import com.android.settingslib.spaprivileged.model.app.userId import com.android.settingslib.spaprivileged.model.app.userId
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
@@ -67,11 +68,12 @@ class PictureInPictureListModel(private val context: Context) :
} }
override fun transformItem(app: ApplicationInfo): PictureInPictureRecord { override fun transformItem(app: ApplicationInfo): PictureInPictureRecord {
val packageInfo =
packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
return createPictureInPictureRecord( return createPictureInPictureRecord(
app = app, app = app,
isSupport = packageInfo.supportsPictureInPicture(), isSupport = app.installed &&
packageManager
.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
.supportsPictureInPicture(),
) )
} }

View File

@@ -160,30 +160,6 @@ public class UtilsTest {
Utils.maybeInitializeVolume(storageManager, new Bundle()); Utils.maybeInitializeVolume(storageManager, new Bundle());
} }
@Test
public void getInstallationStatus_notInstalled_shouldReturnUninstalled() {
assertThat(Utils.getInstallationStatus(new ApplicationInfo()))
.isEqualTo(R.string.not_installed);
}
@Test
public void getInstallationStatus_enabled_shouldReturnInstalled() {
final ApplicationInfo info = new ApplicationInfo();
info.flags = ApplicationInfo.FLAG_INSTALLED;
info.enabled = true;
assertThat(Utils.getInstallationStatus(info)).isEqualTo(R.string.installed);
}
@Test
public void getInstallationStatus_disabled_shouldReturnDisabled() {
final ApplicationInfo info = new ApplicationInfo();
info.flags = ApplicationInfo.FLAG_INSTALLED;
info.enabled = false;
assertThat(Utils.getInstallationStatus(info)).isEqualTo(R.string.disabled);
}
@Test @Test
public void isProfileOrDeviceOwner_deviceOwnerApp_returnTrue() { public void isProfileOrDeviceOwner_deviceOwnerApp_returnTrue() {
when(mDevicePolicyManager.isDeviceOwnerAppOnAnyUser(PACKAGE_NAME)).thenReturn(true); when(mDevicePolicyManager.isDeviceOwnerAppOnAnyUser(PACKAGE_NAME)).thenReturn(true);

View File

@@ -147,6 +147,7 @@ class AllAppListTest {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) } val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val disabledApp = ApplicationInfo().apply { val disabledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
enabled = false enabled = false
} }
@@ -159,6 +160,23 @@ class AllAppListTest {
assertThat(summaryState.value).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled") assertThat(summaryState.value).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
} }
@Test
fun allAppListModel_getSummaryWhenNotInstalled() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val notInstalledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
lateinit var summaryState: State<String>
composeTestRule.setContent {
summaryState =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = notInstalledApp))
}
assertThat(summaryState.value)
.isEqualTo("$SUMMARY${System.lineSeparator()}Not installed for this user")
}
private fun getAppListInput(): AppListInput<AppRecordWithSize> { private fun getAppListInput(): AppListInput<AppRecordWithSize> {
lateinit var input: AppListInput<AppRecordWithSize> lateinit var input: AppListInput<AppRecordWithSize>
composeTestRule.setContent { composeTestRule.setContent {
@@ -192,6 +210,7 @@ class AllAppListTest {
const val SUMMARY = "Summary" const val SUMMARY = "Summary"
val APP = ApplicationInfo().apply { val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
} }
} }
} }

View File

@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.onRoot
@@ -72,7 +73,7 @@ class AppNotificationPreferenceTest {
@Test @Test
fun title_displayed() { fun title_displayed() {
setContent() setContent(APP)
composeTestRule.onNodeWithText(context.getString(R.string.notifications_label)) composeTestRule.onNodeWithText(context.getString(R.string.notifications_label))
.assertIsDisplayed() .assertIsDisplayed()
@@ -80,14 +81,25 @@ class AppNotificationPreferenceTest {
@Test @Test
fun summary_displayed() { fun summary_displayed() {
setContent() setContent(APP)
composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed() composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
} }
@Test
fun whenNotInstalled_disable() {
setContent(ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
})
composeTestRule.onNodeWithText(context.getString(R.string.notifications_label))
.assertIsNotEnabled()
}
@Test @Test
fun onClick_startActivity() { fun onClick_startActivity() {
setContent() setContent(APP)
composeTestRule.onRoot().performClick() composeTestRule.onRoot().performClick()
composeTestRule.delay() composeTestRule.delay()
@@ -102,10 +114,10 @@ class AppNotificationPreferenceTest {
} }
} }
private fun setContent() { private fun setContent(app: ApplicationInfo) {
composeTestRule.setContent { composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) { CompositionLocalProvider(LocalContext provides context) {
AppNotificationPreference(app = APP, repository = repository) AppNotificationPreference(app = app, repository = repository)
} }
} }
} }
@@ -116,6 +128,7 @@ class AppNotificationPreferenceTest {
val APP = ApplicationInfo().apply { val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME packageName = PACKAGE_NAME
uid = UID uid = UID
flags = ApplicationInfo.FLAG_INSTALLED
} }
const val SUMMARY = "Summary" const val SUMMARY = "Summary"
} }

View File

@@ -167,6 +167,7 @@ class PictureInPictureTest {
const val PICTURE_IN_PICTURE_PACKAGE_NAME = "picture.in.picture.package.name" const val PICTURE_IN_PICTURE_PACKAGE_NAME = "picture.in.picture.package.name"
val PICTURE_IN_PICTURE_APP = ApplicationInfo().apply { val PICTURE_IN_PICTURE_APP = ApplicationInfo().apply {
packageName = PICTURE_IN_PICTURE_PACKAGE_NAME packageName = PICTURE_IN_PICTURE_PACKAGE_NAME
flags = ApplicationInfo.FLAG_INSTALLED
} }
val PICTURE_IN_PICTURE_PACKAGE_INFO = PackageInfo().apply { val PICTURE_IN_PICTURE_PACKAGE_INFO = PackageInfo().apply {
packageName = PICTURE_IN_PICTURE_PACKAGE_NAME packageName = PICTURE_IN_PICTURE_PACKAGE_NAME