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

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

Merge "Invoke pin verification upon switching safe search options" into main

parents 7222dd32 0730c541
Loading
Loading
Loading
Loading
+37 −6
Original line number Diff line number Diff line
@@ -15,11 +15,19 @@
 */
package com.android.settings.supervision

import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.VisibleForTesting
import androidx.preference.Preference
import com.android.settings.R
import com.android.settingslib.datastore.Permissions
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.metadata.SensitivityLevel
@@ -30,7 +38,15 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference
/** Base class of web content filters SafeSearch preferences. */
sealed class SupervisionSafeSearchPreference(
    protected val dataStore: SupervisionSafeSearchDataStore
) : BooleanValuePreference, SelectorWithWidgetPreference.OnClickListener, PreferenceBinding {
) :
    BooleanValuePreference,
    SelectorWithWidgetPreference.OnClickListener,
    PreferenceBinding,
    PreferenceLifecycleProvider {
    private lateinit var lifeCycleContext: PreferenceLifecycleContext

    private lateinit var supervisionCredentialLauncher: ActivityResultLauncher<Intent>

    override fun storage(context: Context) = dataStore

    override fun getReadPermissions(context: Context) = Permissions.EMPTY
@@ -52,12 +68,15 @@ sealed class SupervisionSafeSearchPreference(

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

    override fun onRadioButtonClicked(emiter: SelectorWithWidgetPreference) {
        emiter.parent?.forEachRecursively {
            if (it is SelectorWithWidgetPreference) {
                it.isChecked = it == emiter
            }
    override fun onCreate(context: PreferenceLifecycleContext) {
        lifeCycleContext = context
        supervisionCredentialLauncher =
            context.registerForActivityResult(StartActivityForResult(), ::onConfirmCredentials)
    }

    override fun onRadioButtonClicked(emiter: SelectorWithWidgetPreference) {
        val intent = Intent(lifeCycleContext, ConfirmSupervisionCredentialsActivity::class.java)
        supervisionCredentialLauncher.launch(intent)
    }

    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
@@ -67,6 +86,18 @@ sealed class SupervisionSafeSearchPreference(
            it.setOnClickListener(this)
        }
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    fun onConfirmCredentials(result: ActivityResult) {
        if (result.resultCode == Activity.RESULT_OK) {
            val preference = lifeCycleContext.findPreference<SelectorWithWidgetPreference>(key)
            preference?.parent?.forEachRecursively {
                if (it is SelectorWithWidgetPreference) {
                    it.isChecked = it.key == key
                }
            }
        }
    }
}

/** The SafeSearch filter on preference. */
+78 −2
Original line number Diff line number Diff line
@@ -15,13 +15,20 @@
 */
package com.android.settings.supervision

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.provider.Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED
import android.provider.Settings.SettingNotFoundException
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.preference.Preference
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
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
@@ -29,10 +36,20 @@ import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify

@RunWith(AndroidJUnit4::class)
class SupervisionSafeSearchPreferenceTest {
    private val context: Context = ApplicationProvider.getApplicationContext()

    private lateinit var mockLifeCycleContext: PreferenceLifecycleContext
    private lateinit var mockActivityResultLauncher: ActivityResultLauncher<Intent>
    private lateinit var dataStore: SupervisionSafeSearchDataStore
    private lateinit var searchFilterOffPreference: SupervisionSearchFilterOffPreference
    private lateinit var searchFilterOnPreference: SupervisionSearchFilterOnPreference
@@ -40,8 +57,14 @@ class SupervisionSafeSearchPreferenceTest {
    @Before
    fun setUp() {
        dataStore = SupervisionSafeSearchDataStore(context)
        mockLifeCycleContext = mock(PreferenceLifecycleContext::class.java)
        mockActivityResultLauncher =
            mock(ActivityResultLauncher::class.java) as ActivityResultLauncher<Intent>
        mockConfirmSupervisionCredentialsActivity()
        searchFilterOffPreference = SupervisionSearchFilterOffPreference(dataStore)
        searchFilterOffPreference.onCreate(mockLifeCycleContext)
        searchFilterOnPreference = SupervisionSearchFilterOnPreference(dataStore)
        searchFilterOnPreference.onCreate(mockLifeCycleContext)
    }

    @Test
@@ -84,6 +107,25 @@ class SupervisionSafeSearchPreferenceTest {
        assertThat(getFilterOnWidget().isChecked).isTrue()
    }

    @Test
    fun clickBlockExplicitSites_failedToEnablesFilter_activityFailed() {
        Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 0)
        val filterOnWidget = getFilterOnWidget()
        assertThat(filterOnWidget.isChecked).isFalse()

        filterOnWidget.performClick()
        verifyConfirmSupervisionCredentialsActivity()
        searchFilterOnPreference.onConfirmCredentials(
            ActivityResult(Activity.RESULT_CANCELED, null)
        )

        assertThat(
                Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED)
            )
            .isEqualTo(0)
        assertThat(filterOnWidget.isChecked).isFalse()
    }

    @Test
    fun clickBlockExplicitSites_enablesFilter() {
        Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 0)
@@ -91,6 +133,8 @@ class SupervisionSafeSearchPreferenceTest {
        assertThat(filterOnWidget.isChecked).isFalse()

        filterOnWidget.performClick()
        verifyConfirmSupervisionCredentialsActivity()
        searchFilterOnPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))

        assertThat(
                Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED)
@@ -106,6 +150,8 @@ class SupervisionSafeSearchPreferenceTest {
        assertThat(filterOffWidget.isChecked).isFalse()

        filterOffWidget.performClick()
        verifyConfirmSupervisionCredentialsActivity()
        searchFilterOffPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null))

        assertThat(
                Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED)
@@ -115,10 +161,40 @@ class SupervisionSafeSearchPreferenceTest {
    }

    private fun getFilterOnWidget(): SelectorWithWidgetPreference {
        return searchFilterOnPreference.createAndBindWidget(context)
        val widget: SelectorWithWidgetPreference =
            searchFilterOnPreference.createAndBindWidget(context)
        mockLifeCycleContext.stub {
            on { findPreference<Preference>(SupervisionSearchFilterOnPreference.KEY) } doReturn
                widget
            on { requirePreference<Preference>(SupervisionSearchFilterOnPreference.KEY) } doReturn
                widget
        }
        return widget
    }

    private fun getFilterOffWidget(): SelectorWithWidgetPreference {
        return searchFilterOffPreference.createAndBindWidget(context)
        val widget: SelectorWithWidgetPreference =
            searchFilterOffPreference.createAndBindWidget(context)
        mockLifeCycleContext.stub {
            on { findPreference<Preference>(SupervisionSearchFilterOffPreference.KEY) } doReturn
                widget
            on { requirePreference<Preference>(SupervisionSearchFilterOffPreference.KEY) } doReturn
                widget
        }
        return widget
    }

    private fun mockConfirmSupervisionCredentialsActivity() {
        `when`(mockLifeCycleContext.registerForActivityResult(any<StartActivityForResult>(), any()))
            .thenReturn(mockActivityResultLauncher)
    }

    private fun verifyConfirmSupervisionCredentialsActivity() {
        val intentCaptor = argumentCaptor<Intent>()
        verify(mockActivityResultLauncher).launch(intentCaptor.capture())

        assertThat(intentCaptor.allValues.size).isEqualTo(1)
        assertThat(intentCaptor.firstValue.component?.className)
            .isEqualTo(ConfirmSupervisionCredentialsActivity::class.java.name)
    }
}
+44 −3
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Shadows.shadowOf

@RunWith(AndroidJUnit4::class)
class SupervisionWebContentFiltersScreenTest {
@@ -133,7 +134,39 @@ class SupervisionWebContentFiltersScreenTest {

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_WEB_CONTENT_FILTERS_SCREEN)
    fun switchSafeSearchPreferences() {
    fun switchSafeSearchPreferences_succeedWithParentPin() {
        FragmentScenario.launchInContainer(supervisionWebContentFiltersScreen.fragmentClass())
            .onFragment { fragment ->
                val searchFilterOffWidget =
                    fragment.findPreference<SelectorWithWidgetPreference>(
                        SupervisionSearchFilterOffPreference.KEY
                    )!!
                val searchFilterOnWidget =
                    fragment.findPreference<SelectorWithWidgetPreference>(
                        SupervisionSearchFilterOnPreference.KEY
                    )!!

                assertThat(searchFilterOffWidget.isChecked).isTrue()
                assertThat(searchFilterOnWidget.isChecked).isFalse()

                searchFilterOnWidget.performClick()

                // Pretend the PIN verification succeeded.
                val activity = shadowOf(fragment.activity)
                activity.receiveResult(
                    activity.nextStartedActivityForResult.intent,
                    Activity.RESULT_OK,
                    null,
                )

                assertThat(searchFilterOnWidget.isChecked).isTrue()
                assertThat(searchFilterOffWidget.isChecked).isFalse()
            }
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_WEB_CONTENT_FILTERS_SCREEN)
    fun switchSafeSearchPreferences_failedWithParentPin() {
        FragmentScenario.launchInContainer(supervisionWebContentFiltersScreen.fragmentClass())
            .onFragment { fragment ->
                val searchFilterOffPreference =
@@ -150,8 +183,16 @@ class SupervisionWebContentFiltersScreenTest {

                searchFilterOnPreference.performClick()

                assertThat(searchFilterOnPreference.isChecked).isTrue()
                assertThat(searchFilterOffPreference.isChecked).isFalse()
                // Pretend the PIN verification failed.
                val activity = shadowOf(fragment.activity)
                activity.receiveResult(
                    activity.nextStartedActivityForResult.intent,
                    Activity.RESULT_CANCELED,
                    null,
                )

                assertThat(searchFilterOnPreference.isChecked).isFalse()
                assertThat(searchFilterOffPreference.isChecked).isTrue()
            }
    }
}