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

Commit 8cc25271 authored by Xiaomiao Zhang's avatar Xiaomiao Zhang Committed by Android (Google) Code Review
Browse files

Merge "Convert supervision safe sites radio button group to a switch." into main

parents 63855095 6228f688
Loading
Loading
Loading
Loading
+2 −6
Original line number Diff line number Diff line
@@ -14497,12 +14497,8 @@ Data usage charges may apply.</string>
    <string name="supervision_web_content_filters_title">Web content filters</string>
    <!-- Title for web content filters browser category [CHAR LIMIT=60] -->
    <string name="supervision_web_content_filters_browser_title">Websites</string>
    <!-- Title for web content filters browser category block explicit sites option [CHAR LIMIT=60] -->
    <string name="supervision_web_content_filters_browser_block_explicit_sites_title">Block explicit web content</string>
    <!-- Summary for web content filters browser category block explicit sites option [CHAR LIMIT=NONE] -->
    <string name="supervision_web_content_filters_browser_block_explicit_sites_summary">For browsers on this device</string>
    <!-- Title for web content filters browser category allow all sites option [CHAR LIMIT=60] -->
    <string name="supervision_web_content_filters_browser_allow_all_sites_title">Allow all sites</string>
    <!-- Title for web content filters browser filters [CHAR LIMIT=60] -->
    <string name="supervision_web_content_filters_browser_filter_title">Block explicit sites</string>
    <!-- Title for web content filters search category [CHAR LIMIT=60] -->
    <string name="supervision_web_content_filters_search_title">Search results</string>
    <!-- Title for web content filters search filters switch [CHAR LIMIT=60] -->
+4 −19
Original line number Diff line number Diff line
@@ -34,28 +34,14 @@ class SupervisionSafeSitesDataStore(
    override fun contains(key: String) = settingsStore.contains(BROWSER_CONTENT_FILTERS_ENABLED)

    override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
        val settingValue = settingsStore.getInt(BROWSER_CONTENT_FILTERS_ENABLED)
        val isFilterOff: Boolean = settingValue == null || settingValue <= 0
        return when (key) {
            SupervisionAllowAllSitesPreference.KEY -> isFilterOff

            SupervisionBlockExplicitSitesPreference.KEY -> !isFilterOff

            else -> null
        }
            as T?
        val settingValue: Int? = settingsStore.getInt(BROWSER_CONTENT_FILTERS_ENABLED)
        return (settingValue != null && (settingValue > 0)) as T?
    }

    override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
        if (value !is Boolean) return
        when (key) {
            SupervisionAllowAllSitesPreference.KEY ->
                settingsStore.setBoolean(BROWSER_CONTENT_FILTERS_ENABLED, !value)

            SupervisionBlockExplicitSitesPreference.KEY ->
        settingsStore.setBoolean(BROWSER_CONTENT_FILTERS_ENABLED, value)
    }
    }

    override fun onFirstObserverAdded() {
        // observe the underlying storage key
@@ -64,8 +50,7 @@ class SupervisionSafeSitesDataStore(

    override fun onKeyChanged(key: String, reason: Int) {
        // forward data change to preference hierarchy key
        notifyChange(SupervisionBlockExplicitSitesPreference.KEY, reason)
        notifyChange(SupervisionAllowAllSitesPreference.KEY, reason)
        notifyChange(SupervisionSafeSitesSwitchPreference.KEY, reason)
    }

    override fun onLastObserverRemoved() {
+30 −50
Original line number Diff line number Diff line
@@ -25,31 +25,31 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.VisibleForTesting
import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
import com.android.settings.R
import com.android.settings.metrics.PreferenceActionMetricsProvider
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.BooleanValuePreference
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.BooleanValuePreferenceBinding
import com.android.settingslib.metadata.SwitchPreference
import com.android.settingslib.preference.SwitchPreferenceBinding
import com.android.settingslib.supervision.SupervisionIntentProvider
import com.android.settingslib.widget.SelectorWithWidgetPreference

/** Base class of web content filters Safe sites preferences. */
sealed class SupervisionSafeSitesPreference(
    protected val dataStore: SupervisionSafeSitesDataStore
) :
    BooleanValuePreference,
    BooleanValuePreferenceBinding,
    PreferenceActionMetricsProvider,
    SelectorWithWidgetPreference.OnClickListener,

/** Web content filters browser filter preference. */
class SupervisionSafeSitesSwitchPreference(protected val dataStore: SupervisionSafeSitesDataStore) :
    SwitchPreference(KEY),
    SwitchPreferenceBinding,
    Preference.OnPreferenceChangeListener,
    PreferenceLifecycleProvider {
    private lateinit var lifeCycleContext: PreferenceLifecycleContext

    private lateinit var supervisionCredentialLauncher: ActivityResultLauncher<Intent>

    override val title
        get() = R.string.supervision_web_content_filters_browser_filter_title

    override fun storage(context: Context) = dataStore

    override fun getReadPermissions(context: Context) = SettingsSecureStore.getReadPermissions()
@@ -62,68 +62,48 @@ sealed class SupervisionSafeSitesPreference(
    override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) =
        ReadWritePermit.DISALLOW

    override fun createWidget(context: Context) = SelectorWithWidgetPreference(context)

    override fun onCreate(context: PreferenceLifecycleContext) {
        lifeCycleContext = context
        supervisionCredentialLauncher =
            context.registerForActivityResult(StartActivityForResult(), ::onConfirmCredentials)
    }

    override fun onRadioButtonClicked(emitter: SelectorWithWidgetPreference) {
    override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
        if (newValue !is Boolean) return true

        val intent =
            SupervisionIntentProvider.getConfirmSupervisionCredentialsIntent(lifeCycleContext)
        if (intent != null) {
            supervisionCredentialLauncher.launch(intent)
        }
        return false
    }

    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
        super.bind(preference, metadata)
        (preference as SelectorWithWidgetPreference).setOnClickListener(this)
        preference.onPreferenceChangeListener = this
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    fun onConfirmCredentials(result: ActivityResult) {
        if (result.resultCode == Activity.RESULT_OK) {
            lifeCycleContext.requirePreference<SelectorWithWidgetPreference>(key).isChecked = true
            val preference = lifeCycleContext.requirePreference<SwitchPreferenceCompat>(key)
            val isChecked = preference.isChecked
            preference.isChecked = !isChecked
            logMetrics(preference)
        }
    }
}

/** The "Try to block explicit sites" preference. */
class SupervisionBlockExplicitSitesPreference(dataStore: SupervisionSafeSitesDataStore) :
    SupervisionSafeSitesPreference(dataStore) {
    override val key
        get() = KEY

    override val title
        get() = R.string.supervision_web_content_filters_browser_block_explicit_sites_title

    override val summary
        get() = R.string.supervision_web_content_filters_browser_block_explicit_sites_summary

    override val preferenceActionMetrics: Int
        get() = ACTION_SUPERVISION_BLOCK_EXPLICIT_SITES

    companion object {
        const val KEY = "web_content_filters_browser_block_explicit_sites"
    }
    private fun logMetrics(preference: SwitchPreferenceCompat) {
        val isChecked = preference.isChecked
        val metricsFeatureProvider = FeatureFactory.featureFactory.metricsFeatureProvider
        val action =
            if (isChecked) ACTION_SUPERVISION_BLOCK_EXPLICIT_SITES
            else ACTION_SUPERVISION_ALLOW_ALL_SITES
        metricsFeatureProvider.action(preference.context, action)
    }

/** The "Allow all sites" preference. */
class SupervisionAllowAllSitesPreference(dataStore: SupervisionSafeSitesDataStore) :
    SupervisionSafeSitesPreference(dataStore) {
    override val key
        get() = KEY

    override val title
        get() = R.string.supervision_web_content_filters_browser_allow_all_sites_title

    override val preferenceActionMetrics: Int
        get() = ACTION_SUPERVISION_ALLOW_ALL_SITES

    companion object {
        const val KEY = "web_content_filters_browser_allow_all_sites"
        const val KEY = "web_content_filters_browser_filter"
    }
}
+1 −2
Original line number Diff line number Diff line
@@ -71,8 +71,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc
            ) +=
                {
                    val dataStore = SupervisionSafeSitesDataStore(context)
                    +SupervisionBlockExplicitSitesPreference(dataStore)
                    +SupervisionAllowAllSitesPreference(dataStore)
                    +SupervisionSafeSitesSwitchPreference(dataStore)
                }
            +PreferenceCategory(
                SEARCH_RADIO_BUTTON_GROUP,
+40 −56
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
package com.android.settings.supervision

import android.app.Activity
import android.app.settings.SettingsEnums.ACTION_SUPERVISION_ALLOW_ALL_SITES
import android.app.settings.SettingsEnums.ACTION_SUPERVISION_BLOCK_EXPLICIT_SITES
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -25,6 +27,7 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
@@ -32,7 +35,6 @@ import com.android.settings.testutils.MetricsRule
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.preference.createAndBindWidget
import com.android.settingslib.widget.SelectorWithWidgetPreference
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -49,13 +51,12 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions

@RunWith(AndroidJUnit4::class)
class SupervisionSafeSitesPreferenceTest {
class SupervisionSafeSitesSwitchPreferenceTest {
    @get:Rule val metricsRule = MetricsRule()

    private val context: Context = ApplicationProvider.getApplicationContext()
    private val dataStore = SupervisionSafeSitesDataStore(context)
    private val allowAllSitesPreference = SupervisionAllowAllSitesPreference(dataStore)
    private val blockExplicitSitesPreference = SupervisionBlockExplicitSitesPreference(dataStore)
    private val switchPreference = SupervisionSafeSitesSwitchPreference(dataStore)

    private val mockActivityResultLauncher: ActivityResultLauncher<Intent> = mock()
    private val mockPackageManager: PackageManager = mock {
@@ -70,109 +71,92 @@ class SupervisionSafeSitesPreferenceTest {

    @Before
    fun setUp() {
        allowAllSitesPreference.onCreate(mockLifeCycleContext)
        blockExplicitSitesPreference.onCreate(mockLifeCycleContext)
        switchPreference.onCreate(mockLifeCycleContext)
    }

    @Test
    fun getTitle_allowAllSites() {
        assertThat(allowAllSitesPreference.title)
            .isEqualTo(R.string.supervision_web_content_filters_browser_allow_all_sites_title)
    }

    @Test
    fun getTitle_blockExplicitSites() {
        assertThat(blockExplicitSitesPreference.title)
            .isEqualTo(R.string.supervision_web_content_filters_browser_block_explicit_sites_title)
    }

    @Test
    fun getSummary_blockExplicitSites() {
        assertThat(blockExplicitSitesPreference.summary)
            .isEqualTo(
                R.string.supervision_web_content_filters_browser_block_explicit_sites_summary
            )
    fun getTitle() {
        assertThat(switchPreference.title)
            .isEqualTo(R.string.supervision_web_content_filters_browser_filter_title)
    }

    @Test
    fun allowAllSitesIsChecked_whenNoValueIsSet() {
        setDataStoreValue(null)
        assertThat(blockExplicitSitesPreference.createWidget().isChecked).isFalse()
        assertThat(allowAllSitesPreference.createWidget().isChecked).isTrue()
        assertThat(switchPreference.createWidget().isChecked).isFalse()
    }

    @Test
    fun blockExplicitSitesIsChecked_whenPreviouslyEnabled() {
        setDataStoreValue(1)
        assertThat(allowAllSitesPreference.createWidget().isChecked).isFalse()
        assertThat(blockExplicitSitesPreference.createWidget().isChecked).isTrue()
        assertThat(switchPreference.createWidget().isChecked).isTrue()
    }

    @Test
    fun clickBlockExplicitSites_credentialFailed() {
    fun blockExplicitSites_credentialFailed() {
        setDataStoreValue(0)
        val blockExplicitSitesWidget = blockExplicitSitesPreference.createWidget()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
        val filterWidget = switchPreference.createWidget()
        assertThat(filterWidget.isChecked).isFalse()

        blockExplicitSitesWidget.performClick()
        filterWidget.performClick()

        verifyConfirmSupervisionCredentialsActivity()
        blockExplicitSitesPreference.onConfirmCredentials(
            ActivityResult(Activity.RESULT_CANCELED, null)
        )
        switchPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_CANCELED, null))

        verifyNoInteractions(metricsRule.metricsFeatureProvider)
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
        assertThat(filterWidget.isChecked).isFalse()
        assertThat(getDataStoreValue()).isFalse()
    }

    @Test
    fun clickBlockExplicitSites_unresolvedIntent_activityNotLaunched() {
    fun blockExplicitSites_unresolvedIntent_activityNotLaunched() {
        mockPackageManager.stub {
            on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn listOf()
        }

        setDataStoreValue(0)
        val blockExplicitSitesWidget = blockExplicitSitesPreference.createWidget()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
        val filterWidget = switchPreference.createWidget()
        assertThat(filterWidget.isChecked).isFalse()

        blockExplicitSitesWidget.performClick()
        filterWidget.performClick()

        verify(mockActivityResultLauncher, never()).launch(any())
        verifyNoInteractions(metricsRule.metricsFeatureProvider)
        assertThat(getDataStoreValue()).isFalse()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
        assertThat(filterWidget.isChecked).isFalse()
    }

    @Test
    fun clickBlockExplicitSites_enablesFilter() {
    fun blockExplicitSites_enablesFilter() {
        setDataStoreValue(-1)
        val blockExplicitSitesWidget = blockExplicitSitesPreference.createWidget()
        assertThat(blockExplicitSitesWidget.isChecked).isFalse()
        val filterWidget = switchPreference.createWidget()
        assertThat(filterWidget.isChecked).isFalse()

        blockExplicitSitesWidget.performClick()
        filterWidget.performClick()

        verifyConfirmSupervisionCredentialsActivity()
        blockExplicitSitesPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))
        switchPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))

        assertThat(blockExplicitSitesWidget.isChecked).isTrue()
        assertThat(filterWidget.isChecked).isTrue()
        assertThat(getDataStoreValue()).isTrue()
        verify(metricsRule.metricsFeatureProvider).changed(0, blockExplicitSitesPreference.key, 1)
        verify(metricsRule.metricsFeatureProvider)
            .action(context, ACTION_SUPERVISION_BLOCK_EXPLICIT_SITES)
    }

    @Test
    fun clickAllowAllSites_disablesFilter() {
    fun allowAllSites_disablesFilter() {
        setDataStoreValue(1)
        val allowAllSitesWidget = allowAllSitesPreference.createWidget()
        assertThat(allowAllSitesWidget.isChecked).isFalse()
        allowAllSitesWidget.performClick()
        val filterWidget = switchPreference.createWidget()
        assertThat(filterWidget.isChecked).isTrue()
        filterWidget.performClick()

        verifyConfirmSupervisionCredentialsActivity()
        allowAllSitesPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))
        switchPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))

        assertThat(allowAllSitesWidget.isChecked).isTrue()
        assertThat(filterWidget.isChecked).isFalse()
        assertThat(getDataStoreValue()).isFalse()
        verify(metricsRule.metricsFeatureProvider).changed(0, allowAllSitesPreference.key, 1)
        verify(metricsRule.metricsFeatureProvider)
            .action(context, ACTION_SUPERVISION_ALLOW_ALL_SITES)
    }

    private fun getDataStoreValue() =
@@ -182,8 +166,8 @@ class SupervisionSafeSitesPreferenceTest {
        SettingsSecureStore.get(context).setInt(BROWSER_CONTENT_FILTERS_ENABLED, value)
    }

    private fun SupervisionSafeSitesPreference.createWidget() =
        createAndBindWidget<SelectorWithWidgetPreference>(context).also { widget ->
    private fun SupervisionSafeSitesSwitchPreference.createWidget() =
        createAndBindWidget<SwitchPreferenceCompat>(context).also { widget ->
            mockLifeCycleContext.stub { on { requirePreference<Preference>(key) } doReturn widget }
        }

Loading