Loading src/com/android/settings/supervision/SupervisionSafeSearchDataStore.kt +3 −4 Original line number Diff line number Diff line Loading @@ -27,12 +27,11 @@ import com.android.settingslib.datastore.SettingsStore /** Datastore of the safe search preference. */ @Suppress("UNCHECKED_CAST") class SupervisionSafeSearchDataStore( private val context: Context, context: Context, private val settingsStore: SettingsStore = SettingsSecureStore.get(context), ) : AbstractKeyedDataObservable<String>(), KeyedObserver<String>, KeyValueStore { override fun contains(key: String) = key == SupervisionSearchFilterOnPreference.KEY || key == SupervisionSearchFilterOffPreference.KEY override fun contains(key: String) = settingsStore.contains(SEARCH_CONTENT_FILTERS_ENABLED) override fun <T : Any> getValue(key: String, valueType: Class<T>): T? { val settingValue = settingsStore.getInt(SEARCH_CONTENT_FILTERS_ENABLED) Loading src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt +21 −27 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.settings.supervision import android.app.Activity import android.app.settings.SettingsEnums import android.content.Context import android.content.Intent import androidx.activity.result.ActivityResult Loading @@ -24,48 +25,42 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo import androidx.annotation.VisibleForTesting import androidx.preference.Preference import com.android.settings.R import com.android.settingslib.datastore.Permissions import com.android.settings.metrics.PreferenceActionMetricsProvider 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.metadata.SensitivityLevel import com.android.settingslib.preference.PreferenceBinding import com.android.settingslib.preference.forEachRecursively import com.android.settingslib.preference.BooleanValuePreferenceBinding import com.android.settingslib.supervision.SupervisionIntentProvider import com.android.settingslib.widget.SelectorWithWidgetPreference /** Base class of web content filters Search filter preferences. */ sealed class SupervisionSafeSearchPreference( protected val dataStore: SupervisionSafeSearchDataStore private val dataStore: SupervisionSafeSearchDataStore ) : BooleanValuePreference, BooleanValuePreferenceBinding, PreferenceActionMetricsProvider, 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 override fun getReadPermissions(context: Context) = SettingsSecureStore.getReadPermissions() override fun getWritePermissions(context: Context) = Permissions.EMPTY override fun getWritePermissions(context: Context) = SettingsSecureStore.getWritePermissions() override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW override fun getWritePermit( context: Context, value: Boolean?, callingPid: Int, callingUid: Int, ) = ReadWritePermit.DISALLOW override val sensitivityLevel get() = SensitivityLevel.NO_SENSITIVITY override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.DISALLOW override fun createWidget(context: Context) = SelectorWithWidgetPreference(context) Loading @@ -85,21 +80,14 @@ sealed class SupervisionSafeSearchPreference( override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) (preference as SelectorWithWidgetPreference).also { it.isChecked = (dataStore.getBoolean(it.key) == true) it.setOnClickListener(this) } (preference as SelectorWithWidgetPreference).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 } } // Update checked state with dataStore also works but it will bypass metrics logging lifeCycleContext.requirePreference<SelectorWithWidgetPreference>(key).isChecked = true } } } Loading @@ -117,6 +105,9 @@ class SupervisionSearchFilterOnPreference(dataStore: SupervisionSafeSearchDataSt override val summary get() = R.string.supervision_web_content_filters_search_filter_on_summary override val preferenceActionMetrics: Int get() = SettingsEnums.ACTION_SUPERVISION_SEARCH_FILTER_ON companion object { const val KEY = "web_content_filters_search_filter_on" } Loading @@ -132,6 +123,9 @@ class SupervisionSearchFilterOffPreference(dataStore: SupervisionSafeSearchDataS override val title get() = R.string.supervision_web_content_filters_search_filter_off_title override val preferenceActionMetrics: Int get() = SettingsEnums.ACTION_SUPERVISION_SEARCH_FILTER_OFF companion object { const val KEY = "web_content_filters_search_filter_off" } Loading tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt +52 −83 Original line number Diff line number Diff line Loading @@ -20,9 +20,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo 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 Loading @@ -30,46 +28,53 @@ 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.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.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt 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.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions @RunWith(AndroidJUnit4::class) class SupervisionSafeSearchPreferenceTest { @get:Rule val metricsRule = MetricsRule() private val context: Context = ApplicationProvider.getApplicationContext() private val dataStore = SupervisionSafeSearchDataStore(context) private val searchFilterOffPreference = SupervisionSearchFilterOffPreference(dataStore) private val searchFilterOnPreference = SupervisionSearchFilterOnPreference(dataStore) private val mockActivityResultLauncher: ActivityResultLauncher<Intent> = mock() private val mockPackageManager: PackageManager = mock { on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn listOf(ResolveInfo()) } private val mockLifeCycleContext: PreferenceLifecycleContext = mock { on { packageManager } doReturn mockPackageManager on { registerForActivityResult(any<StartActivityForResult>(), any()) } doReturn mockActivityResultLauncher } private lateinit var mockLifeCycleContext: PreferenceLifecycleContext private lateinit var mockActivityResultLauncher: ActivityResultLauncher<Intent> private lateinit var mockPackageManager: PackageManager private lateinit var dataStore: SupervisionSafeSearchDataStore private lateinit var searchFilterOffPreference: SupervisionSearchFilterOffPreference private lateinit var searchFilterOnPreference: SupervisionSearchFilterOnPreference private var dataStoreValue: Int? get() = SettingsSecureStore.get(context).getInt(SEARCH_CONTENT_FILTERS_ENABLED) set(value) = SettingsSecureStore.get(context).setInt(SEARCH_CONTENT_FILTERS_ENABLED, value) @Before fun setUp() { dataStore = SupervisionSafeSearchDataStore(context) mockLifeCycleContext = mock(PreferenceLifecycleContext::class.java) mockActivityResultLauncher = mock(ActivityResultLauncher::class.java) as ActivityResultLauncher<Intent> mockPackageManager = mock(PackageManager::class.java) mockConfirmSupervisionCredentialsActivity() searchFilterOffPreference = SupervisionSearchFilterOffPreference(dataStore) searchFilterOffPreference.onCreate(mockLifeCycleContext) searchFilterOnPreference = SupervisionSearchFilterOnPreference(dataStore) searchFilterOnPreference.onCreate(mockLifeCycleContext) } Loading @@ -93,24 +98,22 @@ class SupervisionSafeSearchPreferenceTest { @Test fun filterOffIsChecked_whenNoValueIsSet() { assertThrows(SettingNotFoundException::class.java) { Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) } assertThat(getFilterOnWidget().isChecked).isFalse() assertThat(getFilterOffWidget().isChecked).isTrue() dataStoreValue = null assertThat(searchFilterOnPreference.createWidget().isChecked).isFalse() assertThat(searchFilterOffPreference.createWidget().isChecked).isTrue() } @Test fun filterOnIsChecked_whenPreviouslyEnabled() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 1) assertThat(getFilterOffWidget().isChecked).isFalse() assertThat(getFilterOnWidget().isChecked).isTrue() dataStoreValue = 1 assertThat(searchFilterOnPreference.createWidget().isChecked).isTrue() assertThat(searchFilterOffPreference.createWidget().isChecked).isFalse() } @Test fun clickFilterOn_failedToEnablesFilter_activityFailed() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 0) val filterOnWidget = getFilterOnWidget() dataStoreValue = 0 val filterOnWidget = searchFilterOnPreference.createWidget() assertThat(filterOnWidget.isChecked).isFalse() filterOnWidget.performClick() Loading @@ -119,96 +122,62 @@ class SupervisionSafeSearchPreferenceTest { ActivityResult(Activity.RESULT_CANCELED, null) ) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(0) verifyNoInteractions(metricsRule.metricsFeatureProvider) assertThat(dataStoreValue).isEqualTo(0) assertThat(filterOnWidget.isChecked).isFalse() } @Test fun clickFilterOn_unresolvedIntent_activityNotLaunched() { `when`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt())) .thenReturn(emptyList<ResolveInfo>()) mockPackageManager.stub { on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn listOf() } Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 0) val filterOnWidget = getFilterOnWidget() dataStoreValue = 0 val filterOnWidget = searchFilterOnPreference.createWidget() assertThat(filterOnWidget.isChecked).isFalse() filterOnWidget.performClick() verify(mockActivityResultLauncher, never()).launch(any()) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(0) verifyNoInteractions(metricsRule.metricsFeatureProvider) assertThat(dataStoreValue).isEqualTo(0) assertThat(filterOnWidget.isChecked).isFalse() } @Test fun clickFilterOn_enablesFilter() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, -1) val filterOnWidget = getFilterOnWidget() dataStoreValue = -1 val filterOnWidget = searchFilterOnPreference.createWidget() assertThat(filterOnWidget.isChecked).isFalse() filterOnWidget.performClick() verifyConfirmSupervisionCredentialsActivity() searchFilterOnPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null)) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(1) assertThat(dataStoreValue).isEqualTo(1) assertThat(filterOnWidget.isChecked).isTrue() verify(metricsRule.metricsFeatureProvider).changed(0, searchFilterOnPreference.key, 1) } @Test fun clickFilterOff_disablesFilter() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 1) val filterOffWidget = getFilterOffWidget() dataStoreValue = 1 val filterOffWidget = searchFilterOffPreference.createWidget() assertThat(filterOffWidget.isChecked).isFalse() filterOffWidget.performClick() verifyConfirmSupervisionCredentialsActivity() searchFilterOffPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null)) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(0) assertThat(dataStoreValue).isEqualTo(0) assertThat(filterOffWidget.isChecked).isTrue() verify(metricsRule.metricsFeatureProvider).changed(0, searchFilterOffPreference.key, 1) } private fun getFilterOnWidget(): SelectorWithWidgetPreference { 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 { 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`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt())) .thenReturn(listOf(ResolveInfo())) `when`(mockLifeCycleContext.packageManager).thenReturn(mockPackageManager) `when`(mockLifeCycleContext.registerForActivityResult(any<StartActivityForResult>(), any())) .thenReturn(mockActivityResultLauncher) private fun SupervisionSafeSearchPreference.createWidget() = createAndBindWidget<SelectorWithWidgetPreference>(context).also { widget -> mockLifeCycleContext.stub { on { requirePreference<Preference>(key) } doReturn widget } } private fun verifyConfirmSupervisionCredentialsActivity() { Loading tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt +7 −3 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.settings.R import com.android.settings.supervision.ipc.SupervisionMessengerClient import com.android.settings.testutils.SettingsStoreRule import com.android.settingslib.ipc.MessengerServiceRule import com.android.settingslib.widget.FooterPreference import com.android.settingslib.widget.SelectorWithWidgetPreference Loading @@ -47,6 +48,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.LooperMode import org.robolectric.shadows.ShadowLooper import org.robolectric.shadows.ShadowPackageManager @RunWith(AndroidJUnit4::class) Loading @@ -56,9 +58,9 @@ class SupervisionWebContentFiltersScreenTest { private val supervisionWebContentFiltersScreen = SupervisionWebContentFiltersScreen() private lateinit var shadowPackageManager: ShadowPackageManager @get:Rule val setFlagsRule = SetFlagsRule() @get:Rule @get:Rule(order = 0) val setFlagsRule = SetFlagsRule() @get:Rule(order = 1) val settingsStoreRule = SettingsStoreRule() @get:Rule(order = 2) val serviceRule = MessengerServiceRule<SupervisionMessengerClient>( TestSupervisionMessengerService::class.java Loading Loading @@ -192,6 +194,7 @@ class SupervisionWebContentFiltersScreenTest { Activity.RESULT_OK, null, ) ShadowLooper.runUiThreadTasksIncludingDelayedTasks() assertThat(searchFilterOnWidget.isChecked).isTrue() assertThat(searchFilterOffWidget.isChecked).isFalse() Loading Loading @@ -224,6 +227,7 @@ class SupervisionWebContentFiltersScreenTest { Activity.RESULT_CANCELED, null, ) ShadowLooper.runUiThreadTasksIncludingDelayedTasks() assertThat(searchFilterOnPreference.isChecked).isFalse() assertThat(searchFilterOffPreference.isChecked).isTrue() Loading Loading
src/com/android/settings/supervision/SupervisionSafeSearchDataStore.kt +3 −4 Original line number Diff line number Diff line Loading @@ -27,12 +27,11 @@ import com.android.settingslib.datastore.SettingsStore /** Datastore of the safe search preference. */ @Suppress("UNCHECKED_CAST") class SupervisionSafeSearchDataStore( private val context: Context, context: Context, private val settingsStore: SettingsStore = SettingsSecureStore.get(context), ) : AbstractKeyedDataObservable<String>(), KeyedObserver<String>, KeyValueStore { override fun contains(key: String) = key == SupervisionSearchFilterOnPreference.KEY || key == SupervisionSearchFilterOffPreference.KEY override fun contains(key: String) = settingsStore.contains(SEARCH_CONTENT_FILTERS_ENABLED) override fun <T : Any> getValue(key: String, valueType: Class<T>): T? { val settingValue = settingsStore.getInt(SEARCH_CONTENT_FILTERS_ENABLED) Loading
src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt +21 −27 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.settings.supervision import android.app.Activity import android.app.settings.SettingsEnums import android.content.Context import android.content.Intent import androidx.activity.result.ActivityResult Loading @@ -24,48 +25,42 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo import androidx.annotation.VisibleForTesting import androidx.preference.Preference import com.android.settings.R import com.android.settingslib.datastore.Permissions import com.android.settings.metrics.PreferenceActionMetricsProvider 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.metadata.SensitivityLevel import com.android.settingslib.preference.PreferenceBinding import com.android.settingslib.preference.forEachRecursively import com.android.settingslib.preference.BooleanValuePreferenceBinding import com.android.settingslib.supervision.SupervisionIntentProvider import com.android.settingslib.widget.SelectorWithWidgetPreference /** Base class of web content filters Search filter preferences. */ sealed class SupervisionSafeSearchPreference( protected val dataStore: SupervisionSafeSearchDataStore private val dataStore: SupervisionSafeSearchDataStore ) : BooleanValuePreference, BooleanValuePreferenceBinding, PreferenceActionMetricsProvider, 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 override fun getReadPermissions(context: Context) = SettingsSecureStore.getReadPermissions() override fun getWritePermissions(context: Context) = Permissions.EMPTY override fun getWritePermissions(context: Context) = SettingsSecureStore.getWritePermissions() override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW override fun getWritePermit( context: Context, value: Boolean?, callingPid: Int, callingUid: Int, ) = ReadWritePermit.DISALLOW override val sensitivityLevel get() = SensitivityLevel.NO_SENSITIVITY override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.DISALLOW override fun createWidget(context: Context) = SelectorWithWidgetPreference(context) Loading @@ -85,21 +80,14 @@ sealed class SupervisionSafeSearchPreference( override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) (preference as SelectorWithWidgetPreference).also { it.isChecked = (dataStore.getBoolean(it.key) == true) it.setOnClickListener(this) } (preference as SelectorWithWidgetPreference).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 } } // Update checked state with dataStore also works but it will bypass metrics logging lifeCycleContext.requirePreference<SelectorWithWidgetPreference>(key).isChecked = true } } } Loading @@ -117,6 +105,9 @@ class SupervisionSearchFilterOnPreference(dataStore: SupervisionSafeSearchDataSt override val summary get() = R.string.supervision_web_content_filters_search_filter_on_summary override val preferenceActionMetrics: Int get() = SettingsEnums.ACTION_SUPERVISION_SEARCH_FILTER_ON companion object { const val KEY = "web_content_filters_search_filter_on" } Loading @@ -132,6 +123,9 @@ class SupervisionSearchFilterOffPreference(dataStore: SupervisionSafeSearchDataS override val title get() = R.string.supervision_web_content_filters_search_filter_off_title override val preferenceActionMetrics: Int get() = SettingsEnums.ACTION_SUPERVISION_SEARCH_FILTER_OFF companion object { const val KEY = "web_content_filters_search_filter_off" } Loading
tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt +52 −83 Original line number Diff line number Diff line Loading @@ -20,9 +20,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo 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 Loading @@ -30,46 +28,53 @@ 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.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.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt 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.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions @RunWith(AndroidJUnit4::class) class SupervisionSafeSearchPreferenceTest { @get:Rule val metricsRule = MetricsRule() private val context: Context = ApplicationProvider.getApplicationContext() private val dataStore = SupervisionSafeSearchDataStore(context) private val searchFilterOffPreference = SupervisionSearchFilterOffPreference(dataStore) private val searchFilterOnPreference = SupervisionSearchFilterOnPreference(dataStore) private val mockActivityResultLauncher: ActivityResultLauncher<Intent> = mock() private val mockPackageManager: PackageManager = mock { on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn listOf(ResolveInfo()) } private val mockLifeCycleContext: PreferenceLifecycleContext = mock { on { packageManager } doReturn mockPackageManager on { registerForActivityResult(any<StartActivityForResult>(), any()) } doReturn mockActivityResultLauncher } private lateinit var mockLifeCycleContext: PreferenceLifecycleContext private lateinit var mockActivityResultLauncher: ActivityResultLauncher<Intent> private lateinit var mockPackageManager: PackageManager private lateinit var dataStore: SupervisionSafeSearchDataStore private lateinit var searchFilterOffPreference: SupervisionSearchFilterOffPreference private lateinit var searchFilterOnPreference: SupervisionSearchFilterOnPreference private var dataStoreValue: Int? get() = SettingsSecureStore.get(context).getInt(SEARCH_CONTENT_FILTERS_ENABLED) set(value) = SettingsSecureStore.get(context).setInt(SEARCH_CONTENT_FILTERS_ENABLED, value) @Before fun setUp() { dataStore = SupervisionSafeSearchDataStore(context) mockLifeCycleContext = mock(PreferenceLifecycleContext::class.java) mockActivityResultLauncher = mock(ActivityResultLauncher::class.java) as ActivityResultLauncher<Intent> mockPackageManager = mock(PackageManager::class.java) mockConfirmSupervisionCredentialsActivity() searchFilterOffPreference = SupervisionSearchFilterOffPreference(dataStore) searchFilterOffPreference.onCreate(mockLifeCycleContext) searchFilterOnPreference = SupervisionSearchFilterOnPreference(dataStore) searchFilterOnPreference.onCreate(mockLifeCycleContext) } Loading @@ -93,24 +98,22 @@ class SupervisionSafeSearchPreferenceTest { @Test fun filterOffIsChecked_whenNoValueIsSet() { assertThrows(SettingNotFoundException::class.java) { Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) } assertThat(getFilterOnWidget().isChecked).isFalse() assertThat(getFilterOffWidget().isChecked).isTrue() dataStoreValue = null assertThat(searchFilterOnPreference.createWidget().isChecked).isFalse() assertThat(searchFilterOffPreference.createWidget().isChecked).isTrue() } @Test fun filterOnIsChecked_whenPreviouslyEnabled() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 1) assertThat(getFilterOffWidget().isChecked).isFalse() assertThat(getFilterOnWidget().isChecked).isTrue() dataStoreValue = 1 assertThat(searchFilterOnPreference.createWidget().isChecked).isTrue() assertThat(searchFilterOffPreference.createWidget().isChecked).isFalse() } @Test fun clickFilterOn_failedToEnablesFilter_activityFailed() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 0) val filterOnWidget = getFilterOnWidget() dataStoreValue = 0 val filterOnWidget = searchFilterOnPreference.createWidget() assertThat(filterOnWidget.isChecked).isFalse() filterOnWidget.performClick() Loading @@ -119,96 +122,62 @@ class SupervisionSafeSearchPreferenceTest { ActivityResult(Activity.RESULT_CANCELED, null) ) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(0) verifyNoInteractions(metricsRule.metricsFeatureProvider) assertThat(dataStoreValue).isEqualTo(0) assertThat(filterOnWidget.isChecked).isFalse() } @Test fun clickFilterOn_unresolvedIntent_activityNotLaunched() { `when`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt())) .thenReturn(emptyList<ResolveInfo>()) mockPackageManager.stub { on { queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()) } doReturn listOf() } Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 0) val filterOnWidget = getFilterOnWidget() dataStoreValue = 0 val filterOnWidget = searchFilterOnPreference.createWidget() assertThat(filterOnWidget.isChecked).isFalse() filterOnWidget.performClick() verify(mockActivityResultLauncher, never()).launch(any()) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(0) verifyNoInteractions(metricsRule.metricsFeatureProvider) assertThat(dataStoreValue).isEqualTo(0) assertThat(filterOnWidget.isChecked).isFalse() } @Test fun clickFilterOn_enablesFilter() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, -1) val filterOnWidget = getFilterOnWidget() dataStoreValue = -1 val filterOnWidget = searchFilterOnPreference.createWidget() assertThat(filterOnWidget.isChecked).isFalse() filterOnWidget.performClick() verifyConfirmSupervisionCredentialsActivity() searchFilterOnPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null)) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(1) assertThat(dataStoreValue).isEqualTo(1) assertThat(filterOnWidget.isChecked).isTrue() verify(metricsRule.metricsFeatureProvider).changed(0, searchFilterOnPreference.key, 1) } @Test fun clickFilterOff_disablesFilter() { Settings.Secure.putInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED, 1) val filterOffWidget = getFilterOffWidget() dataStoreValue = 1 val filterOffWidget = searchFilterOffPreference.createWidget() assertThat(filterOffWidget.isChecked).isFalse() filterOffWidget.performClick() verifyConfirmSupervisionCredentialsActivity() searchFilterOffPreference.onConfirmCredentials(ActivityResult(Activity.RESULT_OK, null)) assertThat( Settings.Secure.getInt(context.getContentResolver(), SEARCH_CONTENT_FILTERS_ENABLED) ) .isEqualTo(0) assertThat(dataStoreValue).isEqualTo(0) assertThat(filterOffWidget.isChecked).isTrue() verify(metricsRule.metricsFeatureProvider).changed(0, searchFilterOffPreference.key, 1) } private fun getFilterOnWidget(): SelectorWithWidgetPreference { 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 { 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`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt())) .thenReturn(listOf(ResolveInfo())) `when`(mockLifeCycleContext.packageManager).thenReturn(mockPackageManager) `when`(mockLifeCycleContext.registerForActivityResult(any<StartActivityForResult>(), any())) .thenReturn(mockActivityResultLauncher) private fun SupervisionSafeSearchPreference.createWidget() = createAndBindWidget<SelectorWithWidgetPreference>(context).also { widget -> mockLifeCycleContext.stub { on { requirePreference<Preference>(key) } doReturn widget } } private fun verifyConfirmSupervisionCredentialsActivity() { Loading
tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt +7 −3 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.settings.R import com.android.settings.supervision.ipc.SupervisionMessengerClient import com.android.settings.testutils.SettingsStoreRule import com.android.settingslib.ipc.MessengerServiceRule import com.android.settingslib.widget.FooterPreference import com.android.settingslib.widget.SelectorWithWidgetPreference Loading @@ -47,6 +48,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.LooperMode import org.robolectric.shadows.ShadowLooper import org.robolectric.shadows.ShadowPackageManager @RunWith(AndroidJUnit4::class) Loading @@ -56,9 +58,9 @@ class SupervisionWebContentFiltersScreenTest { private val supervisionWebContentFiltersScreen = SupervisionWebContentFiltersScreen() private lateinit var shadowPackageManager: ShadowPackageManager @get:Rule val setFlagsRule = SetFlagsRule() @get:Rule @get:Rule(order = 0) val setFlagsRule = SetFlagsRule() @get:Rule(order = 1) val settingsStoreRule = SettingsStoreRule() @get:Rule(order = 2) val serviceRule = MessengerServiceRule<SupervisionMessengerClient>( TestSupervisionMessengerService::class.java Loading Loading @@ -192,6 +194,7 @@ class SupervisionWebContentFiltersScreenTest { Activity.RESULT_OK, null, ) ShadowLooper.runUiThreadTasksIncludingDelayedTasks() assertThat(searchFilterOnWidget.isChecked).isTrue() assertThat(searchFilterOffWidget.isChecked).isFalse() Loading Loading @@ -224,6 +227,7 @@ class SupervisionWebContentFiltersScreenTest { Activity.RESULT_CANCELED, null, ) ShadowLooper.runUiThreadTasksIncludingDelayedTasks() assertThat(searchFilterOnPreference.isChecked).isFalse() assertThat(searchFilterOffPreference.isChecked).isTrue() Loading