Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0aa5fc83 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Create RestrictedPreference

To apply restriction on Preference.

Bug: 280864229
Test: unit test
Change-Id: I414fd4cbf6bbd72a0b5d5e7dc91d77081c112239
parent 0673fea4
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -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,
+4 −4
Original line number Diff line number Diff line
@@ -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 {
+101 −0
Original line number Diff line number Diff line
/*
 * 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() }
    }
}
+15 −7
Original line number Diff line number Diff line
@@ -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
+5 −8
Original line number Diff line number Diff line
@@ -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()
Loading