From 288714283302b04df36c0bf041ac2aacf5ec5dd7 Mon Sep 17 00:00:00 2001 From: Yvonne Jiang Date: Fri, 21 Mar 2025 08:46:31 -0700 Subject: [PATCH] Update "Manage PIN" entry point. - Updates default icon to outlined version - Makes availability conditional on existence of supervising credential - Does not disable entry point when the main switch is disabled Bug: 405159398 Test: atest SupervisionPinManagementScreenTest Test: atest SupervisionDashboardScreenTest Flag: android.app.supervision.flags.enable_supervision_settings_screen Change-Id: I764a6b767019007a93aacf29ecf47677e16cb058 --- res/drawable/ic_pin_outline.xml | 25 ++++++++ .../settings/supervision/SupervisionHelper.kt | 54 ++++++++++++++++ .../SupervisionMainSwitchPreference.kt | 6 +- .../SupervisionPinManagementScreen.kt | 8 ++- .../SupervisionDashboardScreenTest.kt | 4 +- .../SupervisionPinManagementScreenTest.kt | 62 ++++++++++++++++++- 6 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 res/drawable/ic_pin_outline.xml create mode 100644 src/com/android/settings/supervision/SupervisionHelper.kt diff --git a/res/drawable/ic_pin_outline.xml b/res/drawable/ic_pin_outline.xml new file mode 100644 index 00000000000..d7e5f71ebcd --- /dev/null +++ b/res/drawable/ic_pin_outline.xml @@ -0,0 +1,25 @@ + + + + diff --git a/src/com/android/settings/supervision/SupervisionHelper.kt b/src/com/android/settings/supervision/SupervisionHelper.kt new file mode 100644 index 00000000000..8b9b325cb7c --- /dev/null +++ b/src/com/android/settings/supervision/SupervisionHelper.kt @@ -0,0 +1,54 @@ +/* + * 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.app.KeyguardManager +import android.content.Context +import android.os.UserHandle +import android.os.UserManager +import android.os.UserManager.USER_TYPE_PROFILE_SUPERVISING +import androidx.annotation.VisibleForTesting + +/** Convenience methods for interacting with the supervising user profile. */ +open class SupervisionHelper private constructor(context: Context) { + private val mUserManager = context.getSystemService(UserManager::class.java) + private val mKeyguardManager = context.getSystemService(KeyguardManager::class.java) + + fun getSupervisingUserHandle(): UserHandle? { + for (user in (mUserManager?.users ?: emptyList())) { + if (user.userType.equals(USER_TYPE_PROFILE_SUPERVISING)) { + return user.userHandle + } + } + return null + } + + fun isSupervisingCredentialSet(): Boolean { + val supervisingUserId = getSupervisingUserHandle()?.identifier ?: return false + return mKeyguardManager?.isDeviceSecure(supervisingUserId) ?: false + } + + companion object { + @Volatile @VisibleForTesting var sInstance: SupervisionHelper? = null + + fun getInstance(context: Context): SupervisionHelper { + return sInstance + ?: synchronized(this) { + sInstance ?: SupervisionHelper(context).also { sInstance = it } + } + } + } +} diff --git a/src/com/android/settings/supervision/SupervisionMainSwitchPreference.kt b/src/com/android/settings/supervision/SupervisionMainSwitchPreference.kt index 88afc55147c..57591f9aee5 100644 --- a/src/com/android/settings/supervision/SupervisionMainSwitchPreference.kt +++ b/src/com/android/settings/supervision/SupervisionMainSwitchPreference.kt @@ -83,6 +83,7 @@ class SupervisionMainSwitchPreference(context: Context) : val newValue = !supervisionMainSwitchStorage.getBoolean(KEY)!! mainSwitchPreference.setChecked(newValue) updateDependentPreferencesEnabledState(mainSwitchPreference, newValue) + context.notifyPreferenceChange(SupervisionPinManagementScreen.KEY) } return true @@ -110,10 +111,7 @@ class SupervisionMainSwitchPreference(context: Context) : isChecked: Boolean, ) { preference?.parent?.forEachRecursively { - if ( - it.parent?.key == SupervisionDashboardScreen.SUPERVISION_DYNAMIC_GROUP_1 || - it.key == SupervisionPinManagementScreen.KEY - ) { + if (it.parent?.key == SupervisionDashboardScreen.SUPERVISION_DYNAMIC_GROUP_1) { it.isEnabled = isChecked } } diff --git a/src/com/android/settings/supervision/SupervisionPinManagementScreen.kt b/src/com/android/settings/supervision/SupervisionPinManagementScreen.kt index 6d9874a69b7..f3cb2e3c782 100644 --- a/src/com/android/settings/supervision/SupervisionPinManagementScreen.kt +++ b/src/com/android/settings/supervision/SupervisionPinManagementScreen.kt @@ -17,16 +17,20 @@ package com.android.settings.supervision import android.content.Context import com.android.settings.R +import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceScreenCreator /** Pin Management landing page (Settings > Supervision > Manage Pin). */ @ProvidePreferenceScreen(SupervisionPinManagementScreen.KEY) -class SupervisionPinManagementScreen : PreferenceScreenCreator { +class SupervisionPinManagementScreen : PreferenceScreenCreator, PreferenceAvailabilityProvider { override val key: String get() = KEY + override fun isAvailable(context: Context) = + SupervisionHelper.getInstance(context).isSupervisingCredentialSet() + override val title: Int get() = R.string.supervision_pin_management_preference_title @@ -36,7 +40,7 @@ class SupervisionPinManagementScreen : PreferenceScreenCreator { // TODO(b/391994031): dynamically update the icon according to PIN status. override val icon: Int - get() = R.drawable.ic_pin + get() = R.drawable.ic_pin_outline override fun fragmentClass() = SupervisionPinManagementFragment::class.java diff --git a/tests/robotests/src/com/android/settings/supervision/SupervisionDashboardScreenTest.kt b/tests/robotests/src/com/android/settings/supervision/SupervisionDashboardScreenTest.kt index bf579d9fd89..c112b934c0d 100644 --- a/tests/robotests/src/com/android/settings/supervision/SupervisionDashboardScreenTest.kt +++ b/tests/robotests/src/com/android/settings/supervision/SupervisionDashboardScreenTest.kt @@ -65,7 +65,7 @@ class SupervisionDashboardScreenTest { val mainSwitchPreference = fragment.findPreference(SupervisionMainSwitchPreference.KEY)!! val childPreference = - fragment.findPreference(SupervisionPinManagementScreen.KEY)!! + fragment.findPreference(SupervisionWebContentFiltersScreen.KEY)!! assertThat(childPreference.isEnabled).isFalse() @@ -89,7 +89,7 @@ class SupervisionDashboardScreenTest { val mainSwitchPreference = fragment.findPreference(SupervisionMainSwitchPreference.KEY)!! val childPreference = - fragment.findPreference(SupervisionPinManagementScreen.KEY)!! + fragment.findPreference(SupervisionWebContentFiltersScreen.KEY)!! assertThat(childPreference.isEnabled).isFalse() diff --git a/tests/robotests/src/com/android/settings/supervision/SupervisionPinManagementScreenTest.kt b/tests/robotests/src/com/android/settings/supervision/SupervisionPinManagementScreenTest.kt index 90719fba908..294f0ec0cf6 100644 --- a/tests/robotests/src/com/android/settings/supervision/SupervisionPinManagementScreenTest.kt +++ b/tests/robotests/src/com/android/settings/supervision/SupervisionPinManagementScreenTest.kt @@ -15,25 +15,73 @@ */ package com.android.settings.supervision +import android.app.KeyguardManager import android.content.Context +import android.content.ContextWrapper +import android.content.pm.UserInfo +import android.os.UserManager +import android.os.UserManager.USER_TYPE_PROFILE_SUPERVISING import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R import com.google.common.truth.Truth.assertThat +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class SupervisionPinManagementScreenTest { - private val context: Context = ApplicationProvider.getApplicationContext() + private val mockKeyguardManager = mock() + private val mockUserManager = mock() + + private val context: Context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getSystemService(name: String): Any? = + when (name) { + Context.KEYGUARD_SERVICE -> mockKeyguardManager + Context.USER_SERVICE -> mockUserManager + else -> super.getSystemService(name) + } + } private val supervisionPinManagementScreen = SupervisionPinManagementScreen() + @Before + fun setup() { + SupervisionHelper.sInstance = null + } + @Test fun key() { assertThat(supervisionPinManagementScreen.key).isEqualTo(SupervisionPinManagementScreen.KEY) } + @Test + fun isAvailable() { + whenever(mockUserManager.users).thenReturn(listOf(SUPERVISING_USER_INFO)) + whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true) + + assertThat(supervisionPinManagementScreen.isAvailable(context)).isTrue() + } + + @Test + fun isAvailable_noSupervisingUser_returnsFalse() { + whenever(mockUserManager.users).thenReturn(emptyList()) + whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true) + + assertThat(supervisionPinManagementScreen.isAvailable(context)).isFalse() + } + + @Test + fun isAvailable_noSupervisingCredential_returnsFalse() { + whenever(mockUserManager.users).thenReturn(listOf(SUPERVISING_USER_INFO)) + whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(false) + + assertThat(supervisionPinManagementScreen.isAvailable(context)).isFalse() + } + @Test fun getTitle() { assertThat(supervisionPinManagementScreen.title) @@ -45,4 +93,16 @@ class SupervisionPinManagementScreenTest { assertThat(supervisionPinManagementScreen.summary) .isEqualTo(R.string.supervision_pin_management_preference_summary_add) } + + private companion object { + const val SUPERVISING_USER_ID = 5 + val SUPERVISING_USER_INFO = + UserInfo( + SUPERVISING_USER_ID, + /* name */ "supervising", + /* iconPath */ "", + /* flags */ 0, + USER_TYPE_PROFILE_SUPERVISING, + ) + } }