diff options
4 files changed, 90 insertions, 99 deletions
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt index 7bd7fbe0c526..06b3eabfad26 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt @@ -16,9 +16,7 @@ package com.android.settingslib.spaprivileged.template.app -import android.app.AppOpsManager.MODE_ALLOWED -import android.app.AppOpsManager.MODE_DEFAULT -import android.app.AppOpsManager.MODE_ERRORED +import android.app.AppOpsManager import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable @@ -64,7 +62,7 @@ abstract class AppOpPermissionListModel( */ open val permissionHasAppOpFlag: Boolean = true - open val modeForNotAllowed: Int = MODE_ERRORED + open val modeForNotAllowed: Int = AppOpsManager.MODE_ERRORED /** * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed. @@ -130,27 +128,14 @@ abstract class AppOpPermissionListModel( override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) = recordListFlow.filterItem(::isChangeable) - /** - * Defining the default behavior as permissible as long as the package requested this permission - * (This means pre-M gets approval during install time; M apps gets approval during runtime). - */ @Composable - override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? { - if (record.hasRequestBroaderPermission) { - // Broader permission trumps the specific permission. - return { true } - } - - val mode = record.appOpsController.mode.observeAsState() - return { - when (mode.value) { - null -> null - MODE_ALLOWED -> true - MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) } - else -> false - } - } - } + override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? = + isAllowed( + record = record, + appOpsController = record.appOpsController, + permission = permission, + packageManagers = packageManagers, + ) override fun isChangeable(record: AppOpPermissionRecord) = record.hasRequestPermission && @@ -161,3 +146,33 @@ abstract class AppOpPermissionListModel( record.appOpsController.setAllowed(newAllowed) } } + +/** + * Defining the default behavior as permissible as long as the package requested this permission + * (This means pre-M gets approval during install time; M apps gets approval during runtime). + */ +@Composable +internal fun isAllowed( + record: AppOpPermissionRecord, + appOpsController: IAppOpsController, + permission: String, + packageManagers: IPackageManagers = PackageManagers, +): () -> Boolean? { + if (record.hasRequestBroaderPermission) { + // Broader permission trumps the specific permission. + return { true } + } + + val mode = appOpsController.mode.observeAsState() + return { + when (mode.value) { + null -> null + AppOpsManager.MODE_ALLOWED -> true + AppOpsManager.MODE_DEFAULT -> { + with(packageManagers) { record.app.hasGrantPermission(permission) } + } + + else -> false + } + } +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt index 3380b7db4cd0..565543614866 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt @@ -16,19 +16,16 @@ package com.android.settingslib.spaprivileged.template.app -import android.content.Context import android.content.pm.ApplicationInfo import android.os.Bundle import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.core.os.bundleOf +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavType import androidx.navigation.navArgument import com.android.settingslib.spa.framework.common.SettingsEntry @@ -49,6 +46,8 @@ import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvid import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn internal class TogglePermissionAppInfoPageProvider( private val appListTemplate: TogglePermissionAppListTemplate, @@ -132,7 +131,7 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp @VisibleForTesting @Composable -internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage( +internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionAppInfoPage( packageName: String, userId: Int, packageManagers: IPackageManagers = PackageManagers, @@ -145,40 +144,34 @@ internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfo footerContent = { AnnotatedText(footerResId) }, packageManagers = packageManagers, ) { - val model = createSwitchModel(checkNotNull(applicationInfo)) + val app = applicationInfo ?: return@AppInfoPage + val record = rememberRecord(app).value ?: return@AppInfoPage + val isAllowed = isAllowed(record) + val isChangeable by rememberIsChangeable(record) + val switchModel = object : SwitchPreferenceModel { + override val title = stringResource(switchTitleResId) + override val checked = isAllowed + override val changeable = { isChangeable } + override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) } + } val restrictions = Restrictions(userId, switchRestrictionKeys) - RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory) + RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory) } } @Composable -private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel( - app: ApplicationInfo, -): TogglePermissionSwitchModel<T> { - val context = LocalContext.current - val record = remember(app) { transformItem(app) } - val isAllowed = isAllowed(record) - return remember(record) { TogglePermissionSwitchModel(context, this, record, isAllowed) } - .also { model -> LaunchedEffect(model, Dispatchers.IO) { model.initState() } } -} - -private class TogglePermissionSwitchModel<T : AppRecord>( - context: Context, - private val listModel: TogglePermissionAppListModel<T>, - private val record: T, - isAllowed: () -> Boolean?, -) : SwitchPreferenceModel { - private var appChangeable by mutableStateOf(true) +private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberRecord(app: ApplicationInfo) = + remember(app) { + flow { + emit(transformItem(app)) + }.flowOn(Dispatchers.Default) + }.collectAsStateWithLifecycle(initialValue = null) - override val title: String = context.getString(listModel.switchTitleResId) - override val checked = isAllowed - override val changeable = { appChangeable } - fun initState() { - appChangeable = listModel.isChangeable(record) - } - - override val onCheckedChange: (Boolean) -> Unit = { newChecked -> - listModel.setAllowed(record, newChecked) - } -} +@Composable +private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberIsChangeable(record: T) = + remember(record) { + flow { + emit(isChangeable(record)) + }.flowOn(Dispatchers.Default) + }.collectAsStateWithLifecycle(initialValue = false) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt index 3b2fe0f5c16b..d158a2414f85 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt @@ -32,44 +32,37 @@ import com.android.settingslib.spaprivileged.test.R import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.doNothing +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class AppOpPermissionAppListTest { - @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() + @get:Rule + val composeTestRule = createComposeRule() - @get:Rule val composeTestRule = createComposeRule() + private val packageManagers = mock<IPackageManagers>() - @Spy private val context: Context = ApplicationProvider.getApplicationContext() + private val appOpsManager = mock<AppOpsManager>() - @Mock private lateinit var packageManagers: IPackageManagers - - @Mock private lateinit var appOpsManager: AppOpsManager - - @Mock private lateinit var packageManager: PackageManager - - private lateinit var listModel: TestAppOpPermissionAppListModel + private val packageManager = mock<PackageManager> { + doNothing().whenever(mock).updatePermissionFlags(any(), any(), any(), any(), any()) + } - @Before - fun setUp() { - whenever(context.appOpsManager).thenReturn(appOpsManager) - whenever(context.packageManager).thenReturn(packageManager) - doNothing().whenever(packageManager) - .updatePermissionFlags(any(), any(), any(), any(), any()) - listModel = TestAppOpPermissionAppListModel() + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { appOpsManager } doReturn appOpsManager + on { packageManager } doReturn packageManager } + private val listModel = TestAppOpPermissionAppListModel() + @Test fun transformItem_recordHasCorrectApp() { val record = listModel.transformItem(APP) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt index 8bfae14b3b8f..270b3faa7ec6 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt @@ -38,44 +38,34 @@ import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsPro import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider import com.google.common.truth.Truth.assertThat -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule -import org.mockito.kotlin.whenever +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class TogglePermissionAppInfoPageTest { @get:Rule val composeTestRule = createComposeRule() - @get:Rule - val mockito: MockitoRule = MockitoJUnit.rule() - private val context: Context = ApplicationProvider.getApplicationContext() - @Mock - private lateinit var packageManagers: IPackageManagers + private val packageManagers = mock<IPackageManagers> { + on { getPackageInfoAsUser(PACKAGE_NAME, USER_ID) } doReturn PACKAGE_INFO + } private val fakeNavControllerWrapper = FakeNavControllerWrapper() - private val fakeRestrictionsProvider = FakeRestrictionsProvider() + private val fakeRestrictionsProvider = FakeRestrictionsProvider().apply { + restrictedMode = NoRestricted + } private val appListTemplate = TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider)) private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate) - @Before - fun setUp() { - fakeRestrictionsProvider.restrictedMode = NoRestricted - whenever(packageManagers.getPackageInfoAsUser(PACKAGE_NAME, USER_ID)) - .thenReturn(PACKAGE_INFO) - } - @Test fun buildEntry() { val entryList = appInfoPageProvider.buildEntry(null) |