Loading src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt +37 −6 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading @@ -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. */ Loading tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt +78 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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) Loading @@ -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) Loading @@ -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) Loading @@ -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) } } tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt +44 −3 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 = Loading @@ -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() } } } Loading
src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt +37 −6 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) { Loading @@ -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. */ Loading
tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt +78 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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) Loading @@ -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) Loading @@ -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) Loading @@ -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) } }
tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt +44 −3 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 = Loading @@ -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() } } }