Skip to content
Snippets Groups Projects
Commit 4490339b authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "New RestrictedMainSwitchPreference" into main

parents 6b34d83f b4ba27e8
No related branches found
No related tags found
No related merge requests found
......@@ -45,7 +45,7 @@ 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 com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel
import kotlinx.coroutines.flow.Flow
private const val ENTRY_NAME = "AppList"
......@@ -157,7 +157,7 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
}
val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
val allowed = listModel.isAllowed(record)
return RestrictedSwitchPreference.getSummary(
return RestrictedSwitchPreferenceModel.getSummary(
context = context,
restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) },
......
/*
* 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.runtime.Composable
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
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.RestrictedSwitchPreferenceModel.Companion.RestrictedSwitchWrapper
@Composable
fun RestrictedMainSwitchPreference(model: SwitchPreferenceModel, restrictions: Restrictions) {
RestrictedMainSwitchPreference(model, restrictions, ::RestrictionsProviderImpl)
}
@VisibleForTesting
@Composable
internal fun RestrictedMainSwitchPreference(
model: SwitchPreferenceModel,
restrictions: Restrictions,
restrictionsProviderFactory: RestrictionsProviderFactory,
) {
if (restrictions.keys.isEmpty()) {
MainSwitchPreference(model)
return
}
restrictionsProviderFactory.RestrictedSwitchWrapper(model, restrictions) {
MainSwitchPreference(it)
}
}
......@@ -16,29 +16,14 @@
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
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
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
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreferenceModel.Companion.RestrictedSwitchWrapper
@Composable
fun RestrictedSwitchPreference(
......@@ -59,91 +44,7 @@ internal fun RestrictedSwitchPreference(
SwitchPreference(model)
return
}
val context = LocalContext.current
val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
val restrictedSwitchModel = remember(restrictedMode) {
RestrictedSwitchPreferenceModel(context, model, restrictedMode)
}
restrictedSwitchModel.RestrictionWrapper {
SwitchPreference(restrictedSwitchModel)
}
}
internal object RestrictedSwitchPreference {
fun getSummary(
context: Context,
restrictedModeSupplier: () -> RestrictedMode?,
summaryIfNoRestricted: () -> String,
checked: () -> Boolean?,
): () -> String = {
when (val restrictedMode = restrictedModeSupplier()) {
is NoRestricted -> summaryIfNoRestricted()
is BaseUserRestricted -> context.getString(com.android.settingslib.R.string.disabled)
is BlockedByAdmin -> restrictedMode.getSummary(checked())
null -> context.getPlaceholder()
}
}
}
private class RestrictedSwitchPreferenceModel(
context: Context,
model: SwitchPreferenceModel,
private val restrictedMode: RestrictedMode?,
) : SwitchPreferenceModel {
override val title = model.title
override val summary = RestrictedSwitchPreference.getSummary(
context = context,
restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = model.summary,
checked = model.checked,
)
override val checked = when (restrictedMode) {
null -> ({ null })
is NoRestricted -> model.checked
is BaseUserRestricted -> ({ false })
is BlockedByAdmin -> model.checked
}
override val changeable = when (restrictedMode) {
null -> ({ false })
is NoRestricted -> model.changeable
is BaseUserRestricted -> ({ false })
is BlockedByAdmin -> ({ false })
}
override val onCheckedChange = when (restrictedMode) {
null -> null
is NoRestricted -> model.onCheckedChange
// 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
}
@Composable
fun RestrictionWrapper(content: @Composable () -> Unit) {
if (restrictedMode !is BlockedByAdmin) {
content()
return
}
Box(
Modifier
.clickable(
role = Role.Switch,
onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
)
.semantics {
this.toggleableState = ToggleableState(checked())
},
) { content() }
}
private fun ToggleableState(value: Boolean?) = when (value) {
true -> ToggleableState.On
false -> ToggleableState.Off
null -> ToggleableState.Indeterminate
restrictionsProviderFactory.RestrictedSwitchWrapper(model, restrictions) {
SwitchPreference(it)
}
}
/*
* 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 android.content.Context
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.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
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.rememberRestrictedMode
internal class RestrictedSwitchPreferenceModel(
context: Context,
model: SwitchPreferenceModel,
private val restrictedMode: RestrictedMode?,
) : SwitchPreferenceModel {
override val title = model.title
override val summary = getSummary(
context = context,
restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = model.summary,
checked = model.checked,
)
override val checked = when (restrictedMode) {
null -> ({ null })
is NoRestricted -> model.checked
is BaseUserRestricted -> ({ false })
is BlockedByAdmin -> model.checked
}
override val changeable = if (restrictedMode is NoRestricted) model.changeable else ({ false })
override val onCheckedChange = when (restrictedMode) {
null -> null
is NoRestricted -> model.onCheckedChange
// 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
}
@Composable
fun RestrictionWrapper(content: @Composable () -> Unit) {
if (restrictedMode !is BlockedByAdmin) {
content()
return
}
Box(
Modifier
.clickable(
role = Role.Switch,
onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
)
.semantics {
this.toggleableState = ToggleableState(checked())
},
) { content() }
}
private fun ToggleableState(value: Boolean?) = when (value) {
true -> ToggleableState.On
false -> ToggleableState.Off
null -> ToggleableState.Indeterminate
}
companion object {
@Composable
fun RestrictionsProviderFactory.RestrictedSwitchWrapper(
model: SwitchPreferenceModel,
restrictions: Restrictions,
content: @Composable (SwitchPreferenceModel) -> Unit,
) {
val context = LocalContext.current
val restrictedMode = rememberRestrictedMode(restrictions).value
val restrictedSwitchPreferenceModel = remember(restrictedMode) {
RestrictedSwitchPreferenceModel(context, model, restrictedMode)
}
restrictedSwitchPreferenceModel.RestrictionWrapper {
content(restrictedSwitchPreferenceModel)
}
}
fun getSummary(
context: Context,
restrictedModeSupplier: () -> RestrictedMode?,
summaryIfNoRestricted: () -> String,
checked: () -> Boolean?,
): () -> String = {
when (val restrictedMode = restrictedModeSupplier()) {
is NoRestricted -> summaryIfNoRestricted()
is BaseUserRestricted ->
context.getString(com.android.settingslib.R.string.disabled)
is BlockedByAdmin -> restrictedMode.getSummary(checked())
null -> context.getPlaceholder()
}
}
}
}
/*
* 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.runtime.mutableStateOf
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.isOff
import androidx.compose.ui.test.isOn
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.SwitchPreferenceModel
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 RestrictedMainSwitchPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
private val switchPreferenceModel = object : SwitchPreferenceModel {
override val title = TITLE
private val checkedState = mutableStateOf(true)
override val checked = { checkedState.value }
override val onCheckedChange: (Boolean) -> Unit = { checkedState.value = it }
}
@Test
fun whenRestrictionsKeysIsEmpty_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenRestrictionsKeysIsEmpty_toggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenNoRestricted_enabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@Test
fun whenNoRestricted_toggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = NoRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBaseUserRestricted_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsNotEnabled()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBaseUserRestricted_notToggleable() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = BaseUserRestricted
setContent(restrictions)
composeTestRule.onRoot().performClick()
composeTestRule.onNode(isOff()).assertIsDisplayed()
}
@Test
fun whenBlockedByAdmin_disabled() {
val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
fakeRestrictionsProvider.restrictedMode = fakeBlockedByAdmin
setContent(restrictions)
composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
composeTestRule.onNodeWithText(FakeBlockedByAdmin.SUMMARY).assertDoesNotExist()
composeTestRule.onNode(isOn()).assertIsDisplayed()
}
@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 {
RestrictedMainSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
fakeRestrictionsProvider
}
}
}
private companion object {
const val TITLE = "Title"
const val USER_ID = 0
const val RESTRICTION_KEY = "restriction_key"
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment