summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt19
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt8
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt101
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt22
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt13
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt149
6 files changed, 290 insertions, 22 deletions
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index f54de1514fcf..09cb98e22af3 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -22,6 +22,8 @@ import android.os.UserHandle
import android.os.UserManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
@@ -32,15 +34,15 @@ import kotlinx.coroutines.flow.flowOn
import com.android.settingslib.widget.restricted.R
data class Restrictions(
- val userId: Int,
+ val userId: Int = UserHandle.myUserId(),
val keys: List<String>,
)
sealed interface RestrictedMode
-object NoRestricted : RestrictedMode
+data object NoRestricted : RestrictedMode
-object BaseUserRestricted : RestrictedMode
+data object BaseUserRestricted : RestrictedMode
interface BlockedByAdmin : RestrictedMode {
fun getSummary(checked: Boolean?): String
@@ -79,6 +81,17 @@ interface RestrictionsProvider {
typealias RestrictionsProviderFactory = (Context, Restrictions) -> RestrictionsProvider
+@Composable
+internal fun RestrictionsProviderFactory.rememberRestrictedMode(
+ restrictions: Restrictions,
+): State<RestrictedMode?> {
+ val context = LocalContext.current
+ val restrictionsProvider = remember(restrictions) {
+ this(context, restrictions)
+ }
+ return restrictionsProvider.restrictedModeState()
+}
+
internal class RestrictionsProviderImpl(
private val context: Context,
private val restrictions: Restrictions,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 1fa854a4c09e..17e970845f58 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -45,6 +45,7 @@ import com.android.settingslib.spaprivileged.model.app.userId
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.model.enterprise.rememberRestrictedMode
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.flow.Flow
@@ -149,14 +150,13 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
@Composable
fun getSummary(record: T): State<String> {
- val restrictionsProvider = remember(record.app.userId) {
- val restrictions = Restrictions(
+ val restrictions = remember(record.app.userId) {
+ Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
)
- restrictionsProviderFactory(context, restrictions)
}
- val restrictedMode = restrictionsProvider.restrictedModeState()
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions)
val allowed = listModel.isAllowed(record)
return remember {
derivedStateOf {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
new file mode 100644
index 000000000000..50490c0b887d
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.settingslib.spaprivileged.template.preference
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
+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.RestrictionsProviderFactory
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
+import com.android.settingslib.spaprivileged.model.enterprise.rememberRestrictedMode
+
+@Composable
+fun RestrictedPreference(
+ model: PreferenceModel,
+ restrictions: Restrictions,
+) {
+ RestrictedPreference(model, restrictions, ::RestrictionsProviderImpl)
+}
+
+@VisibleForTesting
+@Composable
+internal fun RestrictedPreference(
+ model: PreferenceModel,
+ restrictions: Restrictions,
+ restrictionsProviderFactory: RestrictionsProviderFactory,
+) {
+ if (restrictions.keys.isEmpty()) {
+ Preference(model)
+ return
+ }
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
+ val restrictedSwitchModel = remember(restrictedMode) {
+ RestrictedPreferenceModel(model, restrictedMode)
+ }
+ restrictedSwitchModel.RestrictionWrapper {
+ Preference(restrictedSwitchModel)
+ }
+}
+
+private class RestrictedPreferenceModel(
+ model: PreferenceModel,
+ private val restrictedMode: RestrictedMode?,
+) : PreferenceModel {
+ override val title = model.title
+ override val summary = model.summary
+ override val icon = model.icon
+
+ override val enabled = when (restrictedMode) {
+ NoRestricted -> model.enabled
+ else -> stateOf(false)
+ }
+
+ override val onClick = when (restrictedMode) {
+ NoRestricted -> model.onClick
+ // Need to passthrough onClick for clickable semantics, although since enabled is false so
+ // this will not be called.
+ BaseUserRestricted -> model.onClick
+ else -> null
+ }
+
+ @Composable
+ fun RestrictionWrapper(content: @Composable () -> Unit) {
+ if (restrictedMode !is BlockedByAdmin) {
+ content()
+ return
+ }
+ Box(
+ Modifier
+ .clickable(
+ role = Role.Button,
+ onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
+ )
+ ) { content() }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index e77dcd4d9cc4..2129403c2d85 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spaprivileged.template.preference
import android.content.Context
+import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
@@ -40,22 +41,29 @@ import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
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.model.enterprise.rememberRestrictedMode
@Composable
fun RestrictedSwitchPreference(
model: SwitchPreferenceModel,
restrictions: Restrictions,
- restrictionsProviderFactory: RestrictionsProviderFactory = ::RestrictionsProviderImpl,
+) {
+ RestrictedSwitchPreference(model, restrictions, ::RestrictionsProviderImpl)
+}
+
+@VisibleForTesting
+@Composable
+internal fun RestrictedSwitchPreference(
+ model: SwitchPreferenceModel,
+ restrictions: Restrictions,
+ restrictionsProviderFactory: RestrictionsProviderFactory,
) {
if (restrictions.keys.isEmpty()) {
SwitchPreference(model)
return
}
val context = LocalContext.current
- val restrictionsProvider = remember(restrictions) {
- restrictionsProviderFactory(context, restrictions)
- }
- val restrictedMode = restrictionsProvider.restrictedModeState().value
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
val restrictedSwitchModel = remember(restrictedMode) {
RestrictedSwitchPreferenceModel(context, model, restrictedMode)
}
@@ -112,8 +120,8 @@ private class RestrictedSwitchPreferenceModel(
override val onCheckedChange = when (restrictedMode) {
null -> null
is NoRestricted -> model.onCheckedChange
- // Need to pass a non null onCheckedChange to enable semantics ToggleableState, although
- // since changeable is false this will not be called.
+ // Need to passthrough onCheckedChange for toggleable semantics, although since changeable
+ // is false so this will not be called.
is BaseUserRestricted -> model.onCheckedChange
// Pass null since semantics ToggleableState is provided in RestrictionWrapper.
is BlockedByAdmin -> null
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
index 86b6f027997d..f9abefc11e24 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
@@ -16,15 +16,15 @@
package com.android.settingslib.spaprivileged.template.scaffold
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
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.model.enterprise.rememberRestrictedMode
@Composable
fun MoreOptionsScope.RestrictedMenuItem(
@@ -35,6 +35,7 @@ fun MoreOptionsScope.RestrictedMenuItem(
RestrictedMenuItemImpl(text, restrictions, onClick, ::RestrictionsProviderImpl)
}
+@VisibleForTesting
@Composable
internal fun MoreOptionsScope.RestrictedMenuItemImpl(
text: String,
@@ -42,12 +43,8 @@ internal fun MoreOptionsScope.RestrictedMenuItemImpl(
onClick: () -> Unit,
restrictionsProviderFactory: RestrictionsProviderFactory,
) {
- val context = LocalContext.current
- val restrictionsProvider = remember(restrictions) {
- restrictionsProviderFactory(context, restrictions)
- }
- val restrictedMode = restrictionsProvider.restrictedModeState().value
- MenuItem(text = text, enabled = restrictedMode !is BaseUserRestricted) {
+ val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
+ MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) {
when (restrictedMode) {
is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent()
else -> onClick()
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt
new file mode 100644
index 000000000000..eadf0ca0686d
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreferenceTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.settingslib.spaprivileged.template.preference
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RestrictedPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+
+ private val fakeRestrictionsProvider = FakeRestrictionsProvider()
+
+ private var clicked = false
+
+ private val preferenceModel = object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = { clicked = true }
+ }
+
+ @Test
+ fun whenRestrictionsKeysIsEmpty_enabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenRestrictionsKeysIsEmpty_clickable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(clicked).isTrue()
+ }
+
+ @Test
+ fun whenNoRestricted_enabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = NoRestricted
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenNoRestricted_clickable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = NoRestricted
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(clicked).isTrue()
+ }
+
+ @Test
+ fun whenBaseUserRestricted_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
+ }
+
+ @Test
+ fun whenBaseUserRestricted_notClickable() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(clicked).isFalse()
+ }
+
+ @Test
+ fun whenBlockedByAdmin_widgetInEnableStateToAllowClick() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenBlockedByAdmin_click() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
+ }
+
+ private fun setContent(restrictions: Restrictions) {
+ composeTestRule.setContent {
+ RestrictedPreference(preferenceModel, restrictions) { _, _ ->
+ fakeRestrictionsProvider
+ }
+ }
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val USER_ID = 0
+ const val RESTRICTION_KEY = "restriction_key"
+ }
+}