Loading src/com/android/settings/supervision/SupervisionBrowserFiltersSupportedAppPreference.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import androidx.preference.Preference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.preference.PreferenceBinding class SupervisionBrowserFiltersSupportedAppPreference( private val titleString: CharSequence?, private val packageName: String, ) : PreferenceMetadata, PreferenceBinding { override val key: String get() = KEY override fun isIndexable(context: Context) = false override fun createWidget(context: Context) = Preference(context, /* attrs= */ null).apply { val packageManager = context.packageManager val icon = packageManager.getApplicationIcon(packageName) title = titleString setIcon(icon) } companion object { const val KEY = "browser_filters_supported_app" } } src/com/android/settings/supervision/SupervisionSearchFiltersSupportedAppPreference.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import androidx.preference.Preference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.preference.PreferenceBinding class SupervisionSearchFiltersSupportedAppPreference( private val titleString: CharSequence?, private val packageName: String, ) : PreferenceMetadata, PreferenceBinding { override val key: String get() = KEY override fun isIndexable(context: Context) = false override fun createWidget(context: Context) = Preference(context, /* attrs= */ null).apply { val packageManager = context.packageManager val icon = packageManager.getApplicationIcon(packageName) title = titleString setIcon(icon) } companion object { const val KEY = "search_filters_supported_app" } } src/com/android/settings/supervision/SupervisionWebContentFiltersScreen.kt +53 −6 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.app.settings.SettingsEnums import android.app.supervision.flags.Flags import android.content.Context import androidx.preference.Preference import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen import com.android.settings.CatalystSettingsActivity import com.android.settings.R Loading Loading @@ -64,6 +65,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc override fun onCreate(context: PreferenceLifecycleContext) { supervisionClient = getSupervisionClient(context) updatePreferenceData(context) addSupportedApps(context) } override fun onDestroy(context: PreferenceLifecycleContext) { Loading @@ -78,7 +80,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc preferenceHierarchy(context, this) { +SupervisionWebContentFiltersTopIntroPreference() +PreferenceCategory( BROWSER_RADIO_BUTTON_GROUP, BROWSER_FILTERS_GROUP, R.string.supervision_web_content_filters_browser_title, ) += { Loading @@ -86,7 +88,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc +SupervisionSafeSitesSwitchPreference(dataStore) } +PreferenceCategory( SEARCH_RADIO_BUTTON_GROUP, SEARCH_FILTERS_GROUP, R.string.supervision_web_content_filters_search_title, ) += { Loading @@ -96,6 +98,9 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc +SupervisionWebContentFiltersFooterPreference() } override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) = makeLaunchIntent(context, SupervisionWebContentFiltersActivity::class.java, metadata?.key) private fun updatePreferenceData(context: PreferenceLifecycleContext) { val preferenceScreen = context.findPreference<Preference>(key) if (preferenceScreen is PreferenceScreen) { Loading @@ -121,15 +126,57 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc } } override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) = makeLaunchIntent(context, SupervisionWebContentFiltersActivity::class.java, metadata?.key) private fun addSupportedApps(context: PreferenceLifecycleContext) { context.lifecycleScope.launch { val supportedAppsMap = withContext(Dispatchers.IO) { supervisionClient?.getSupportedApps( listOf(BROWSER_FILTERS_SUPPORTED_APPS, SEARCH_FILTERS_SUPPORTED_APPS) ) } val browserFilterSupportedApps = supportedAppsMap?.get(BROWSER_FILTERS_SUPPORTED_APPS) ?: emptyList() context.findPreference<PreferenceGroup>(BROWSER_FILTERS_GROUP)?.apply { for (supportedApp in browserFilterSupportedApps) { val packageName = supportedApp.packageName if (packageName != null) { SupervisionBrowserFiltersSupportedAppPreference( supportedApp.title, packageName, ) .createWidget(context) .let { addPreference(it) } } } } val searchFilterSupportedApps = supportedAppsMap?.get(SEARCH_FILTERS_SUPPORTED_APPS) ?: emptyList() context.findPreference<PreferenceGroup>(SEARCH_FILTERS_GROUP)?.apply { for (supportedApp in searchFilterSupportedApps) { val packageName = supportedApp.packageName if (packageName != null) { SupervisionSearchFiltersSupportedAppPreference( supportedApp.title, packageName, ) .createWidget(context) .let { addPreference(it) } } } } } } private fun getSupervisionClient(context: Context) = supervisionClient ?: SupervisionMessengerClient(context).also { supervisionClient = it } companion object { const val KEY = "supervision_web_content_filters" internal const val BROWSER_RADIO_BUTTON_GROUP = "browser_radio_button_group" internal const val SEARCH_RADIO_BUTTON_GROUP = "search_radio_button_group" internal const val BROWSER_FILTERS_GROUP = "browser_filters_group" internal const val BROWSER_FILTERS_SUPPORTED_APPS = "browser_filters_supported_apps" internal const val SEARCH_FILTERS_GROUP = "search_filters_group" internal const val SEARCH_FILTERS_SUPPORTED_APPS = "search_filters_supported_apps" } } tests/robotests/src/com/android/settings/supervision/SupervisionBrowserFiltersSupportedAppPreferenceTest.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.graphics.Color import android.graphics.drawable.ColorDrawable import androidx.preference.Preference import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowPackageManager @RunWith(AndroidJUnit4::class) class SupervisionBrowserFiltersSupportedAppPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() private val title = "title" private val appPackageName = "packageName" private val appIcon = ColorDrawable(Color.RED) private lateinit var preference: Preference private lateinit var shadowPackageManager: ShadowPackageManager @Before fun setUp() { shadowPackageManager = shadowOf(context.packageManager) val applicationInfo = ApplicationInfo().apply { packageName = appPackageName } shadowPackageManager.installPackage( PackageInfo().apply { packageName = appPackageName this.applicationInfo = applicationInfo } ) shadowPackageManager.setApplicationIcon(appPackageName, appIcon) preference = SupervisionBrowserFiltersSupportedAppPreference(title, appPackageName) .createWidget(context) } @Test fun getTitle() { assertThat(preference.title).isEqualTo(title) } @Test fun getIcon() { assertThat(preference.icon).isEqualTo(appIcon) } } tests/robotests/src/com/android/settings/supervision/SupervisionSearchFiltersSupportedAppPreferenceTest.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.graphics.Color import android.graphics.drawable.ColorDrawable import androidx.preference.Preference import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowPackageManager @RunWith(AndroidJUnit4::class) class SupervisionSearchFiltersSupportedAppPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() private val title = "title" private val appPackageName = "packageName" private val appIcon = ColorDrawable(Color.RED) private lateinit var preference: Preference private lateinit var shadowPackageManager: ShadowPackageManager @Before fun setUp() { shadowPackageManager = shadowOf(context.packageManager) val applicationInfo = ApplicationInfo().apply { packageName = appPackageName } shadowPackageManager.installPackage( PackageInfo().apply { packageName = appPackageName this.applicationInfo = applicationInfo } ) shadowPackageManager.setApplicationIcon(appPackageName, appIcon) preference = SupervisionSearchFiltersSupportedAppPreference(title, appPackageName) .createWidget(context) } @Test fun getTitle() { assertThat(preference.title).isEqualTo(title) } @Test fun getIcon() { assertThat(preference.icon).isEqualTo(appIcon) } } Loading
src/com/android/settings/supervision/SupervisionBrowserFiltersSupportedAppPreference.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import androidx.preference.Preference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.preference.PreferenceBinding class SupervisionBrowserFiltersSupportedAppPreference( private val titleString: CharSequence?, private val packageName: String, ) : PreferenceMetadata, PreferenceBinding { override val key: String get() = KEY override fun isIndexable(context: Context) = false override fun createWidget(context: Context) = Preference(context, /* attrs= */ null).apply { val packageManager = context.packageManager val icon = packageManager.getApplicationIcon(packageName) title = titleString setIcon(icon) } companion object { const val KEY = "browser_filters_supported_app" } }
src/com/android/settings/supervision/SupervisionSearchFiltersSupportedAppPreference.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import androidx.preference.Preference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.preference.PreferenceBinding class SupervisionSearchFiltersSupportedAppPreference( private val titleString: CharSequence?, private val packageName: String, ) : PreferenceMetadata, PreferenceBinding { override val key: String get() = KEY override fun isIndexable(context: Context) = false override fun createWidget(context: Context) = Preference(context, /* attrs= */ null).apply { val packageManager = context.packageManager val icon = packageManager.getApplicationIcon(packageName) title = titleString setIcon(icon) } companion object { const val KEY = "search_filters_supported_app" } }
src/com/android/settings/supervision/SupervisionWebContentFiltersScreen.kt +53 −6 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.app.settings.SettingsEnums import android.app.supervision.flags.Flags import android.content.Context import androidx.preference.Preference import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen import com.android.settings.CatalystSettingsActivity import com.android.settings.R Loading Loading @@ -64,6 +65,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc override fun onCreate(context: PreferenceLifecycleContext) { supervisionClient = getSupervisionClient(context) updatePreferenceData(context) addSupportedApps(context) } override fun onDestroy(context: PreferenceLifecycleContext) { Loading @@ -78,7 +80,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc preferenceHierarchy(context, this) { +SupervisionWebContentFiltersTopIntroPreference() +PreferenceCategory( BROWSER_RADIO_BUTTON_GROUP, BROWSER_FILTERS_GROUP, R.string.supervision_web_content_filters_browser_title, ) += { Loading @@ -86,7 +88,7 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc +SupervisionSafeSitesSwitchPreference(dataStore) } +PreferenceCategory( SEARCH_RADIO_BUTTON_GROUP, SEARCH_FILTERS_GROUP, R.string.supervision_web_content_filters_search_title, ) += { Loading @@ -96,6 +98,9 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc +SupervisionWebContentFiltersFooterPreference() } override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) = makeLaunchIntent(context, SupervisionWebContentFiltersActivity::class.java, metadata?.key) private fun updatePreferenceData(context: PreferenceLifecycleContext) { val preferenceScreen = context.findPreference<Preference>(key) if (preferenceScreen is PreferenceScreen) { Loading @@ -121,15 +126,57 @@ open class SupervisionWebContentFiltersScreen : PreferenceScreenMixin, Preferenc } } override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) = makeLaunchIntent(context, SupervisionWebContentFiltersActivity::class.java, metadata?.key) private fun addSupportedApps(context: PreferenceLifecycleContext) { context.lifecycleScope.launch { val supportedAppsMap = withContext(Dispatchers.IO) { supervisionClient?.getSupportedApps( listOf(BROWSER_FILTERS_SUPPORTED_APPS, SEARCH_FILTERS_SUPPORTED_APPS) ) } val browserFilterSupportedApps = supportedAppsMap?.get(BROWSER_FILTERS_SUPPORTED_APPS) ?: emptyList() context.findPreference<PreferenceGroup>(BROWSER_FILTERS_GROUP)?.apply { for (supportedApp in browserFilterSupportedApps) { val packageName = supportedApp.packageName if (packageName != null) { SupervisionBrowserFiltersSupportedAppPreference( supportedApp.title, packageName, ) .createWidget(context) .let { addPreference(it) } } } } val searchFilterSupportedApps = supportedAppsMap?.get(SEARCH_FILTERS_SUPPORTED_APPS) ?: emptyList() context.findPreference<PreferenceGroup>(SEARCH_FILTERS_GROUP)?.apply { for (supportedApp in searchFilterSupportedApps) { val packageName = supportedApp.packageName if (packageName != null) { SupervisionSearchFiltersSupportedAppPreference( supportedApp.title, packageName, ) .createWidget(context) .let { addPreference(it) } } } } } } private fun getSupervisionClient(context: Context) = supervisionClient ?: SupervisionMessengerClient(context).also { supervisionClient = it } companion object { const val KEY = "supervision_web_content_filters" internal const val BROWSER_RADIO_BUTTON_GROUP = "browser_radio_button_group" internal const val SEARCH_RADIO_BUTTON_GROUP = "search_radio_button_group" internal const val BROWSER_FILTERS_GROUP = "browser_filters_group" internal const val BROWSER_FILTERS_SUPPORTED_APPS = "browser_filters_supported_apps" internal const val SEARCH_FILTERS_GROUP = "search_filters_group" internal const val SEARCH_FILTERS_SUPPORTED_APPS = "search_filters_supported_apps" } }
tests/robotests/src/com/android/settings/supervision/SupervisionBrowserFiltersSupportedAppPreferenceTest.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.graphics.Color import android.graphics.drawable.ColorDrawable import androidx.preference.Preference import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowPackageManager @RunWith(AndroidJUnit4::class) class SupervisionBrowserFiltersSupportedAppPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() private val title = "title" private val appPackageName = "packageName" private val appIcon = ColorDrawable(Color.RED) private lateinit var preference: Preference private lateinit var shadowPackageManager: ShadowPackageManager @Before fun setUp() { shadowPackageManager = shadowOf(context.packageManager) val applicationInfo = ApplicationInfo().apply { packageName = appPackageName } shadowPackageManager.installPackage( PackageInfo().apply { packageName = appPackageName this.applicationInfo = applicationInfo } ) shadowPackageManager.setApplicationIcon(appPackageName, appIcon) preference = SupervisionBrowserFiltersSupportedAppPreference(title, appPackageName) .createWidget(context) } @Test fun getTitle() { assertThat(preference.title).isEqualTo(title) } @Test fun getIcon() { assertThat(preference.icon).isEqualTo(appIcon) } }
tests/robotests/src/com/android/settings/supervision/SupervisionSearchFiltersSupportedAppPreferenceTest.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.supervision import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.graphics.Color import android.graphics.drawable.ColorDrawable import androidx.preference.Preference import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Shadows.shadowOf import org.robolectric.shadows.ShadowPackageManager @RunWith(AndroidJUnit4::class) class SupervisionSearchFiltersSupportedAppPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() private val title = "title" private val appPackageName = "packageName" private val appIcon = ColorDrawable(Color.RED) private lateinit var preference: Preference private lateinit var shadowPackageManager: ShadowPackageManager @Before fun setUp() { shadowPackageManager = shadowOf(context.packageManager) val applicationInfo = ApplicationInfo().apply { packageName = appPackageName } shadowPackageManager.installPackage( PackageInfo().apply { packageName = appPackageName this.applicationInfo = applicationInfo } ) shadowPackageManager.setApplicationIcon(appPackageName, appIcon) preference = SupervisionSearchFiltersSupportedAppPreference(title, appPackageName) .createWidget(context) } @Test fun getTitle() { assertThat(preference.title).isEqualTo(title) } @Test fun getIcon() { assertThat(preference.icon).isEqualTo(appIcon) } }