Refactor TogglePermissionAppInfoPageProvider

Also add unit tests.

Bug: 260660819
Test: Unit test
Test: Manually with Settings
Change-Id: Ifb6844f4127f07ea5d211d68806e683144310589
This commit is contained in:
Chaohui Wang
2022-12-05 00:05:48 +08:00
parent e3bda5623b
commit 7459ecff6e
9 changed files with 235 additions and 70 deletions

View File

@@ -74,6 +74,8 @@ interface RestrictionsProvider {
fun restrictedModeState(): State<RestrictedMode?>
}
typealias RestrictionsProviderFactory = (Context, Restrictions) -> RestrictionsProvider
internal class RestrictionsProviderImpl(
private val context: Context,
private val restrictions: Restrictions,

View File

@@ -16,11 +16,12 @@
package com.android.settingslib.spaprivileged.template.app
import android.content.pm.PackageInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Footer
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
@Composable
fun AppInfoPage(
@@ -28,18 +29,16 @@ fun AppInfoPage(
packageName: String,
userId: Int,
footerText: String,
content: @Composable () -> Unit,
packageManagers: IPackageManagers,
content: @Composable PackageInfo.() -> Unit,
) {
val packageInfo = remember(packageName, userId) {
packageManagers.getPackageInfoAsUser(packageName, userId)
} ?: return
RegularScaffold(title = title) {
val appInfoProvider = remember {
PackageManagers.getPackageInfoAsUser(packageName, userId)?.let { packageInfo ->
AppInfoProvider(packageInfo)
}
} ?: return@RegularScaffold
remember(packageInfo) { AppInfoProvider(packageInfo) }.AppInfo(displayVersion = true)
appInfoProvider.AppInfo(displayVersion = true)
content()
packageInfo.content()
Footer(footerText)
}

View File

@@ -19,6 +19,7 @@ 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.State
@@ -38,23 +39,15 @@ import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.toRoute
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.Dispatchers
private const val ENTRY_NAME = "AllowControl"
private const val PERMISSION = "permission"
private const val PACKAGE_NAME = "rt_packageName"
private const val USER_ID = "rt_userId"
private const val PAGE_NAME = "TogglePermissionAppInfoPage"
private val PAGE_PARAMETER = listOf(
navArgument(PERMISSION) { type = NavType.StringType },
navArgument(PACKAGE_NAME) { type = NavType.StringType },
navArgument(USER_ID) { type = NavType.IntType },
)
internal class TogglePermissionAppInfoPageProvider(
private val appListTemplate: TogglePermissionAppListTemplate,
) : SettingsPageProvider {
@@ -64,11 +57,7 @@ internal class TogglePermissionAppInfoPageProvider(
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val owner = SettingsPage.create(name, parameter = parameter, arguments = arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create(ENTRY_NAME, owner).build()
)
return entryList
return listOf(SettingsEntryBuilder.create("AllowControl", owner).build())
}
@Composable
@@ -76,11 +65,22 @@ internal class TogglePermissionAppInfoPageProvider(
val permissionType = arguments?.getString(PERMISSION)!!
val packageName = arguments.getString(PACKAGE_NAME)!!
val userId = arguments.getInt(USER_ID)
val listModel = appListTemplate.rememberModel(permissionType)
TogglePermissionAppInfoPage(listModel, packageName, userId)
appListTemplate.rememberModel(permissionType)
.TogglePermissionAppInfoPage(packageName, userId)
}
companion object {
private const val PAGE_NAME = "TogglePermissionAppInfoPage"
private const val PERMISSION = "permission"
private const val PACKAGE_NAME = "rt_packageName"
private const val USER_ID = "rt_userId"
private val PAGE_PARAMETER = listOf(
navArgument(PERMISSION) { type = NavType.StringType },
navArgument(PACKAGE_NAME) { type = NavType.StringType },
navArgument(USER_ID) { type = NavType.IntType },
)
@Composable
fun navigator(permissionType: String, app: ApplicationInfo) =
navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
@@ -116,43 +116,36 @@ internal class TogglePermissionAppInfoPageProvider(
}
}
@VisibleForTesting
@Composable
private fun TogglePermissionAppInfoPage(
listModel: TogglePermissionAppListModel<out AppRecord>,
internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage(
packageName: String,
userId: Int,
packageManagers: IPackageManagers = PackageManagers,
restrictionsProviderFactory: RestrictionsProviderFactory = ::RestrictionsProviderImpl,
) {
AppInfoPage(
title = stringResource(listModel.pageTitleResId),
title = stringResource(pageTitleResId),
packageName = packageName,
userId = userId,
footerText = stringResource(listModel.footerResId),
footerText = stringResource(footerResId),
packageManagers = packageManagers,
) {
val model = createSwitchModel(listModel, packageName, userId) ?: return@AppInfoPage
LaunchedEffect(model, Dispatchers.Default) {
model.initState()
}
RestrictedSwitchPreference(model, Restrictions(userId, listModel.switchRestrictionKeys))
val model = createSwitchModel(applicationInfo)
val restrictions = Restrictions(userId, switchRestrictionKeys)
RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory)
}
}
@Composable
private fun <T : AppRecord> createSwitchModel(
listModel: TogglePermissionAppListModel<T>,
packageName: String,
userId: Int,
): TogglePermissionSwitchModel<T>? {
val record = remember {
PackageManagers.getApplicationInfoAsUser(packageName, userId)?.let { app ->
listModel.transformItem(app)
}
} ?: return null
private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel(
app: ApplicationInfo,
): TogglePermissionSwitchModel<T> {
val context = LocalContext.current
val isAllowed = listModel.isAllowed(record)
return remember {
TogglePermissionSwitchModel(context, listModel, record, isAllowed)
}
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>(

View File

@@ -38,19 +38,14 @@ import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
@Composable
fun RestrictedSwitchPreference(model: SwitchPreferenceModel, restrictions: Restrictions) {
RestrictedSwitchPreferenceImpl(model, restrictions, ::RestrictionsProviderImpl)
}
@Composable
internal fun RestrictedSwitchPreferenceImpl(
fun RestrictedSwitchPreference(
model: SwitchPreferenceModel,
restrictions: Restrictions,
restrictionsProviderFactory: (Context, Restrictions) -> RestrictionsProvider,
restrictionsProviderFactory: RestrictionsProviderFactory = ::RestrictionsProviderImpl,
) {
if (restrictions.keys.isEmpty()) {
SwitchPreference(model)

View File

@@ -0,0 +1,153 @@
/*
* 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.settingslib.spaprivileged.template.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
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.Mockito.`when` as whenever
@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 fakeRestrictionsProvider = FakeRestrictionsProvider()
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)
assertThat(entryList).hasSize(1)
assertThat(entryList[0].displayName).isEqualTo("AllowControl")
}
@Test
fun title_isDisplayed() {
val listModel = TestTogglePermissionAppListModel()
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
.assertIsDisplayed()
}
@Test
fun whenAllowed_switchIsOn() {
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsOn()
}
@Test
fun whenNotAllowed_switchIsOff() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsOff()
}
@Test
fun whenNotChangeable_switchNotEnabled() {
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
.assertIsDisplayed()
.assertIsNotEnabled()
}
@Test
fun footer_isDisplayed() {
val listModel = TestTogglePermissionAppListModel()
setTogglePermissionAppInfoPage(listModel)
composeTestRule.onNodeWithText(context.getString(listModel.footerResId))
.assertIsDisplayed()
}
private fun setTogglePermissionAppInfoPage(listModel: TestTogglePermissionAppListModel) {
composeTestRule.setContent {
listModel.TogglePermissionAppInfoPage(
packageName = PACKAGE_NAME,
userId = USER_ID,
packageManagers = packageManagers,
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
)
}
}
private companion object {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
val PACKAGE_INFO = PackageInfo().apply {
packageName = PACKAGE_NAME
applicationInfo = APP
}
}
}

View File

@@ -25,7 +25,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.test.R
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.Rule
import org.junit.Test
@@ -70,8 +70,3 @@ class TogglePermissionAppListTest {
assertThat(createPageProviders.any { it is TogglePermissionAppInfoPageProvider }).isTrue()
}
}
private object TestTogglePermissionAppListProvider : TogglePermissionAppListProvider {
override val permissionType = "test.PERMISSION"
override fun createModel(context: Context) = TestTogglePermissionAppListModel()
}

View File

@@ -142,7 +142,7 @@ class RestrictedSwitchPreferenceTest {
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedSwitchPreferenceImpl(switchPreferenceModel, restrictions) { _, _ ->
RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
fakeRestrictionsProvider
}
}

View File

@@ -23,7 +23,10 @@ import com.android.settingslib.spaprivileged.test.R
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
import kotlinx.coroutines.flow.Flow
class TestTogglePermissionAppListModel : TogglePermissionAppListModel<TestAppRecord> {
class TestTogglePermissionAppListModel(
private val isAllowed: Boolean? = null,
private val isChangeable: Boolean = false,
) : TogglePermissionAppListModel<TestAppRecord> {
override val pageTitleResId = R.string.test_permission_title
override val switchTitleResId = R.string.test_permission_switch_title
override val footerResId = R.string.test_permission_footer
@@ -34,9 +37,9 @@ class TestTogglePermissionAppListModel : TogglePermissionAppListModel<TestAppRec
recordListFlow
@Composable
override fun isAllowed(record: TestAppRecord) = stateOf(null)
override fun isAllowed(record: TestAppRecord) = stateOf(isAllowed)
override fun isChangeable(record: TestAppRecord) = false
override fun isChangeable(record: TestAppRecord) = isChangeable
override fun setAllowed(record: TestAppRecord, newAllowed: Boolean) {}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.settingslib.spaprivileged.tests.testutils
import android.content.Context
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object TestTogglePermissionAppListProvider : TogglePermissionAppListProvider {
override val permissionType = "test.PERMISSION"
override fun createModel(context: Context) = TestTogglePermissionAppListModel()
}