Loading src/com/android/settings/supervision/TopLevelSupervisionPreferenceController.kt +2 −58 Original line number Diff line number Diff line Loading @@ -19,82 +19,26 @@ import android.app.supervision.flags.Flags import android.content.Context import android.content.Intent import androidx.preference.Preference import com.android.settings.applications.AppStoreUtil.getAppStoreLink import com.android.settings.applications.AppStoreUtil.getInstallerPackageName import com.android.settings.core.BasePreferenceController import com.android.settings.supervision.ipc.SupervisionMessengerClient.Companion.SUPERVISION_MESSENGER_SERVICE_BIND_ACTION /** Controller for the top level Supervision settings Preference item. */ class TopLevelSupervisionPreferenceController(context: Context, key: String) : BasePreferenceController(context, key) { private val supervisionPackage = context.supervisionPackageName private var missingAppStoreLink = false private var redirectIntent: Intent? = null override fun handlePreferenceTreeClick(preference: Preference): Boolean { if (preference.key == preferenceKey) { val intent = redirectIntent ?: Intent(mContext, SupervisionDashboardActivity::class.java) val intent = Intent(mContext, SupervisionDashboardActivity::class.java) mContext.startActivity(intent) return true } return super.handlePreferenceTreeClick(preference) } override fun updateState(preference: Preference) { super.updateState(preference) if (!hasNecessarySupervisionComponent() && missingAppStoreLink) { preference.isEnabled = false } } override fun getAvailabilityStatus(): Int { if (!Flags.enableSupervisionSettingsScreen() || supervisionPackage == null) if (!Flags.enableSupervisionSettingsScreen()) { return UNSUPPORTED_ON_DEVICE // Try to navigate to app store if supervision app with necessary component is not installed if (!hasNecessarySupervisionComponent()) { val installerPackageName = getInstallerPackageName(mContext, supervisionPackage) val appStoreLinkIntent = installerPackageName?.let { getAppStoreLink(mContext, installerPackageName, supervisionPackage) } if (appStoreLinkIntent == null) { missingAppStoreLink = true return AVAILABLE } missingAppStoreLink = false redirectIntent = appStoreLinkIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } if (hasRedirect()) { redirectIntent = Intent(SETTINGS_REDIRECT_ACTION).setPackage(supervisionPackage) } return AVAILABLE } private fun hasNecessarySupervisionComponent(): Boolean { val intent = Intent(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION).setPackage(supervisionPackage) return supervisionPackage != null && mContext.packageManager.queryIntentServices(intent, 0).isNotEmpty() } private fun hasRedirect(): Boolean { val intent = Intent(SETTINGS_REDIRECT_ACTION).setPackage(supervisionPackage) return supervisionPackage != null && mContext.packageManager .queryIntentActivitiesAsUser(intent, 0, mContext.userId) .isNotEmpty() } companion object { // Supervision app should declare an intent-filter with this action to redirect the settings // navigation target. const val SETTINGS_REDIRECT_ACTION = "android.app.supervision.action.VIEW_SETTINGS" } } tests/robotests/src/com/android/settings/supervision/TopLevelSupervisionPreferenceControllerTest.kt +2 −123 Original line number Diff line number Diff line Loading @@ -16,39 +16,23 @@ package com.android.settings.supervision import android.app.Activity import android.app.role.RoleManager import android.content.Context import android.content.Intent import android.content.pm.InstallSourceInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import androidx.preference.Preference import com.android.settings.core.BasePreferenceController.AVAILABLE import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE import com.android.settings.supervision.TopLevelSupervisionPreferenceController.Companion.SETTINGS_REDIRECT_ACTION import com.android.settings.supervision.ipc.SupervisionMessengerClient.Companion.SUPERVISION_MESSENGER_SERVICE_BIND_ACTION import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.argThat import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class TopLevelSupervisionPreferenceControllerTest { private val mockRoleManager = mock<RoleManager>() private val mockPackageManager = mock<PackageManager>() private val context = spy(Robolectric.buildActivity(Activity::class.java).get()) { on { getSystemService(Context.ROLE_SERVICE) }.thenReturn(mockRoleManager) on { packageManager }.thenReturn(mockPackageManager) } spy(Robolectric.buildActivity(Activity::class.java).get()) private val preference = Preference(context) Loading @@ -58,109 +42,7 @@ class TopLevelSupervisionPreferenceControllerTest { } @Test fun supervisionPackageNameIsNull_returnUnsupported() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) }.thenReturn(listOf<String>()) } val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) verify(mockRoleManager).getRoleHolders(any()) assertThat(preferenceController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE) } @Test fun noNecessaryComponent_noAppStoreLink_preferenceDisabled() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } .thenReturn(listOf(SUPERVISION_PACKAGE_NAME)) } mockPackageManager.stub { on { queryIntentServices( actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION), any<Int>(), ) } .thenReturn(listOf()) } mockPackageManager.stub { on { getInstallSourceInfo(any()) }.thenReturn(InstallSourceInfo(null, null, null, null)) } val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE) preferenceController.handlePreferenceTreeClick(preference) preferenceController.updateState(preference) assertThat(preference.isEnabled).isFalse() verify(context) .startActivity(componentIntentMatcher(SupervisionDashboardActivity::class.java)) } @Test fun hasNecessaryComponent_isFullySupervised_launchFullSupervision() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } .thenReturn(listOf(SUPERVISION_PACKAGE_NAME)) } mockPackageManager.stub { on { queryIntentServices( actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION), any<Int>(), ) } .thenReturn(listOf(ResolveInfo())) } mockPackageManager.stub { on { queryIntentActivitiesAsUser( actionIntentMatcher(SETTINGS_REDIRECT_ACTION), any<Int>(), any<Int>(), ) } .thenReturn(listOf(ResolveInfo())) } val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE) preferenceController.handlePreferenceTreeClick(preference) verify(context).startActivity(actionIntentMatcher(SETTINGS_REDIRECT_ACTION)) } @Test fun hasNecessaryComponent_isNotFullySupervised_returnAvailable() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } .thenReturn(listOf(SUPERVISION_PACKAGE_NAME)) } mockPackageManager.stub { on { queryIntentServices( actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION), any<Int>(), ) } .thenReturn(listOf(ResolveInfo())) } mockPackageManager.stub { on { queryIntentActivitiesAsUser( actionIntentMatcher(SETTINGS_REDIRECT_ACTION), any<Int>(), any<Int>(), ) } .thenReturn(listOf()) } fun navigateToDashboard() { val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE) Loading @@ -170,13 +52,10 @@ class TopLevelSupervisionPreferenceControllerTest { .startActivity(componentIntentMatcher(SupervisionDashboardActivity::class.java)) } private fun actionIntentMatcher(action: String) = argThat<Intent> { this.action == action } private fun componentIntentMatcher(cls: Class<*>) = argThat<Intent> { this.component?.className == cls.name } private companion object { const val SUPERVISION_PACKAGE_NAME = "com.android.supervision" const val PREFERENCE_KEY = "test_key" } } Loading
src/com/android/settings/supervision/TopLevelSupervisionPreferenceController.kt +2 −58 Original line number Diff line number Diff line Loading @@ -19,82 +19,26 @@ import android.app.supervision.flags.Flags import android.content.Context import android.content.Intent import androidx.preference.Preference import com.android.settings.applications.AppStoreUtil.getAppStoreLink import com.android.settings.applications.AppStoreUtil.getInstallerPackageName import com.android.settings.core.BasePreferenceController import com.android.settings.supervision.ipc.SupervisionMessengerClient.Companion.SUPERVISION_MESSENGER_SERVICE_BIND_ACTION /** Controller for the top level Supervision settings Preference item. */ class TopLevelSupervisionPreferenceController(context: Context, key: String) : BasePreferenceController(context, key) { private val supervisionPackage = context.supervisionPackageName private var missingAppStoreLink = false private var redirectIntent: Intent? = null override fun handlePreferenceTreeClick(preference: Preference): Boolean { if (preference.key == preferenceKey) { val intent = redirectIntent ?: Intent(mContext, SupervisionDashboardActivity::class.java) val intent = Intent(mContext, SupervisionDashboardActivity::class.java) mContext.startActivity(intent) return true } return super.handlePreferenceTreeClick(preference) } override fun updateState(preference: Preference) { super.updateState(preference) if (!hasNecessarySupervisionComponent() && missingAppStoreLink) { preference.isEnabled = false } } override fun getAvailabilityStatus(): Int { if (!Flags.enableSupervisionSettingsScreen() || supervisionPackage == null) if (!Flags.enableSupervisionSettingsScreen()) { return UNSUPPORTED_ON_DEVICE // Try to navigate to app store if supervision app with necessary component is not installed if (!hasNecessarySupervisionComponent()) { val installerPackageName = getInstallerPackageName(mContext, supervisionPackage) val appStoreLinkIntent = installerPackageName?.let { getAppStoreLink(mContext, installerPackageName, supervisionPackage) } if (appStoreLinkIntent == null) { missingAppStoreLink = true return AVAILABLE } missingAppStoreLink = false redirectIntent = appStoreLinkIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } if (hasRedirect()) { redirectIntent = Intent(SETTINGS_REDIRECT_ACTION).setPackage(supervisionPackage) } return AVAILABLE } private fun hasNecessarySupervisionComponent(): Boolean { val intent = Intent(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION).setPackage(supervisionPackage) return supervisionPackage != null && mContext.packageManager.queryIntentServices(intent, 0).isNotEmpty() } private fun hasRedirect(): Boolean { val intent = Intent(SETTINGS_REDIRECT_ACTION).setPackage(supervisionPackage) return supervisionPackage != null && mContext.packageManager .queryIntentActivitiesAsUser(intent, 0, mContext.userId) .isNotEmpty() } companion object { // Supervision app should declare an intent-filter with this action to redirect the settings // navigation target. const val SETTINGS_REDIRECT_ACTION = "android.app.supervision.action.VIEW_SETTINGS" } }
tests/robotests/src/com/android/settings/supervision/TopLevelSupervisionPreferenceControllerTest.kt +2 −123 Original line number Diff line number Diff line Loading @@ -16,39 +16,23 @@ package com.android.settings.supervision import android.app.Activity import android.app.role.RoleManager import android.content.Context import android.content.Intent import android.content.pm.InstallSourceInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import androidx.preference.Preference import com.android.settings.core.BasePreferenceController.AVAILABLE import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE import com.android.settings.supervision.TopLevelSupervisionPreferenceController.Companion.SETTINGS_REDIRECT_ACTION import com.android.settings.supervision.ipc.SupervisionMessengerClient.Companion.SUPERVISION_MESSENGER_SERVICE_BIND_ACTION import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.argThat import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class TopLevelSupervisionPreferenceControllerTest { private val mockRoleManager = mock<RoleManager>() private val mockPackageManager = mock<PackageManager>() private val context = spy(Robolectric.buildActivity(Activity::class.java).get()) { on { getSystemService(Context.ROLE_SERVICE) }.thenReturn(mockRoleManager) on { packageManager }.thenReturn(mockPackageManager) } spy(Robolectric.buildActivity(Activity::class.java).get()) private val preference = Preference(context) Loading @@ -58,109 +42,7 @@ class TopLevelSupervisionPreferenceControllerTest { } @Test fun supervisionPackageNameIsNull_returnUnsupported() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) }.thenReturn(listOf<String>()) } val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) verify(mockRoleManager).getRoleHolders(any()) assertThat(preferenceController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE) } @Test fun noNecessaryComponent_noAppStoreLink_preferenceDisabled() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } .thenReturn(listOf(SUPERVISION_PACKAGE_NAME)) } mockPackageManager.stub { on { queryIntentServices( actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION), any<Int>(), ) } .thenReturn(listOf()) } mockPackageManager.stub { on { getInstallSourceInfo(any()) }.thenReturn(InstallSourceInfo(null, null, null, null)) } val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE) preferenceController.handlePreferenceTreeClick(preference) preferenceController.updateState(preference) assertThat(preference.isEnabled).isFalse() verify(context) .startActivity(componentIntentMatcher(SupervisionDashboardActivity::class.java)) } @Test fun hasNecessaryComponent_isFullySupervised_launchFullSupervision() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } .thenReturn(listOf(SUPERVISION_PACKAGE_NAME)) } mockPackageManager.stub { on { queryIntentServices( actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION), any<Int>(), ) } .thenReturn(listOf(ResolveInfo())) } mockPackageManager.stub { on { queryIntentActivitiesAsUser( actionIntentMatcher(SETTINGS_REDIRECT_ACTION), any<Int>(), any<Int>(), ) } .thenReturn(listOf(ResolveInfo())) } val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE) preferenceController.handlePreferenceTreeClick(preference) verify(context).startActivity(actionIntentMatcher(SETTINGS_REDIRECT_ACTION)) } @Test fun hasNecessaryComponent_isNotFullySupervised_returnAvailable() { mockRoleManager.stub { on { getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION) } .thenReturn(listOf(SUPERVISION_PACKAGE_NAME)) } mockPackageManager.stub { on { queryIntentServices( actionIntentMatcher(SUPERVISION_MESSENGER_SERVICE_BIND_ACTION), any<Int>(), ) } .thenReturn(listOf(ResolveInfo())) } mockPackageManager.stub { on { queryIntentActivitiesAsUser( actionIntentMatcher(SETTINGS_REDIRECT_ACTION), any<Int>(), any<Int>(), ) } .thenReturn(listOf()) } fun navigateToDashboard() { val preferenceController = TopLevelSupervisionPreferenceController(context, PREFERENCE_KEY) assertThat(preferenceController.availabilityStatus).isEqualTo(AVAILABLE) Loading @@ -170,13 +52,10 @@ class TopLevelSupervisionPreferenceControllerTest { .startActivity(componentIntentMatcher(SupervisionDashboardActivity::class.java)) } private fun actionIntentMatcher(action: String) = argThat<Intent> { this.action == action } private fun componentIntentMatcher(cls: Class<*>) = argThat<Intent> { this.component?.className == cls.name } private companion object { const val SUPERVISION_PACKAGE_NAME = "com.android.supervision" const val PREFERENCE_KEY = "test_key" } }