Loading src/com/android/settings/supervision/PreferenceDataProvider.kt +1 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.settings.supervision import com.android.settings.supervision.ipc.PreferenceData import kotlinx.coroutines.Deferred /** * Interface for providing preference data. Loading @@ -36,5 +35,5 @@ interface PreferenceDataProvider { * @return A map where the keys are the requested keys, and the values are the corresponding * [PreferenceData] objects. */ suspend fun getPreferenceData(keys: List<String>): Deferred<Map<String, PreferenceData>> suspend fun getPreferenceData(keys: List<String>): Map<String, PreferenceData> } src/com/android/settings/supervision/SupervisionPromoFooterPreference.kt +40 −31 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.settings.supervision import android.content.Context import android.content.Intent import androidx.preference.Preference import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata Loading @@ -24,19 +25,31 @@ import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.PreferenceTitleProvider import com.android.settingslib.preference.PreferenceBinding import com.android.settingslib.widget.CardPreference import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext /** A bottom banner promoting other supervision features offered by the supervision app. */ class SupervisionPromoFooterPreference(private val preferenceDataProvider: PreferenceDataProvider) : class SupervisionPromoFooterPreference( private val preferenceDataProvider: PreferenceDataProvider, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : PreferenceMetadata, PreferenceBinding, PreferenceLifecycleProvider, PreferenceTitleProvider, PreferenceSummaryProvider { /** Whether [intent] holds an initialized value. */ private var initialized = false // Operation to be performed when the preference is clicked private var intent: Intent? = null override val key: String get() = KEY // TODO(b/399497788): Remove this and get title from supervision app override fun getTitle(context: Context) = "Full parental controls" Loading @@ -44,44 +57,40 @@ class SupervisionPromoFooterPreference(private val preferenceDataProvider: Prefe override fun getSummary(context: Context) = "Set up an account for your kid & help them manage it (required for kids under [AOC])" override fun intent(context: Context): Intent? = intent override val key: String get() = KEY override fun createWidget(context: Context) = CardPreference(context) override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) if (initialized) { preference.intent = intent preference.isVisible = intent != null } } override fun onResume(context: PreferenceLifecycleContext) { super.onResume(context) val preference = context.findPreference<CardPreference>(KEY) if (preference != null) { context.lifecycleScope.launch { // TODO(b/399497788) Get title & summary from supervision app. val preferenceData = preferenceDataProvider.getPreferenceData(listOf(KEY)).await()[KEY] intent = intent ?: Intent() intent?.setAction(preferenceData?.action) intent?.setPackage(preferenceData?.targetPackage) // Hide the preference if the target package can not respond to the action if (!canTargetPackageRespondToAction(preference.context)) { preference.isVisible = false withContext(coroutineDispatcher) { preferenceDataProvider.getPreferenceData(listOf(KEY))[KEY] } initialized = true val targetIntent = Intent(preferenceData?.action).apply { `package` = preferenceData?.targetPackage addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } if (targetIntent.isValid(context)) intent = targetIntent context.notifyPreferenceChange(KEY) } } private fun canTargetPackageRespondToAction(context: Context): Boolean { if (intent?.action == null || intent?.`package` == null) { return false } intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val activities = context.packageManager.queryIntentActivitiesAsUser(intent!!, 0, context.userId) return activities.isNotEmpty() } private fun Intent.isValid(context: Context) = action != null && `package` != null && context.packageManager.queryIntentActivitiesAsUser(this, 0, context.userId).isNotEmpty() companion object { const val KEY = "supervision_promo_footer" const val KEY = "promo_footer" } } src/com/android/settings/supervision/ipc/SupervisionMessengerClient.kt +5 −7 Original line number Diff line number Diff line Loading @@ -21,8 +21,6 @@ import android.util.Log import com.android.internal.R import com.android.settings.supervision.PreferenceDataProvider import com.android.settingslib.ipc.MessengerServiceClient import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred /** * A specialized [MessengerServiceClient] for interacting with the system supervision service. Loading Loading @@ -55,17 +53,17 @@ class SupervisionMessengerClient(context: Context) : * @return A map of preference data, where the keys are the preference keys and the values are * [PreferenceData] objects, or an empty map if an error occurs. */ override suspend fun getPreferenceData( keys: List<String> ): Deferred<Map<String, PreferenceData>> = override suspend fun getPreferenceData(keys: List<String>): Map<String, PreferenceData> = try { invoke(supervisionPackageName, PreferenceDataApi(), PreferenceDataRequest(keys = keys)) .await() } catch (e: Exception) { Log.e("MessengerService", "Error fetching Preference data from supervision app", e) CompletableDeferred(mapOf()) Log.e(TAG, "Error fetching Preference data from supervision app", e) mapOf() } companion object { private const val TAG = "SupervisionMessengerClient" private const val SUPERVISION_MESSENGER_SERVICE_BIND_ACTION = "android.app.supervision.action.SUPERVISION_MESSENGER_SERVICE" } Loading tests/robotests/src/com/android/settings/supervision/SupervisionPromoFooterPreferenceTest.kt +68 −120 Original line number Diff line number Diff line Loading @@ -16,80 +16,55 @@ package com.android.settings.supervision import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.lifecycleScope import androidx.lifecycle.testing.TestLifecycleOwner import androidx.preference.Preference import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.settings.supervision.SupervisionPromoFooterPreference.Companion.KEY import com.android.settings.supervision.ipc.PreferenceData import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.getPreferenceSummary import com.android.settingslib.metadata.getPreferenceTitle import com.android.settingslib.widget.CardPreference import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @ExperimentalCoroutinesApi @RunWith(AndroidJUnit4::class) class SupervisionPromoFooterPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() private val preference = CardPreference(context) @get:Rule val mocks: MockitoRule = MockitoJUnit.rule() private var preferenceData: PreferenceData? = null private val testDispatcher = UnconfinedTestDispatcher() private val testScope = TestScope(testDispatcher) private lateinit var preference: CardPreference private lateinit var context: Context private lateinit var lifecycleCoroutineScope: LifecycleCoroutineScope @Mock private lateinit var preferenceLifecycleContext: PreferenceLifecycleContext @Mock private lateinit var preferenceDataProvider: PreferenceDataProvider @Mock private lateinit var mockPackageManager: PackageManager @Before fun setup() { context = spy(InstrumentationRegistry.getInstrumentation().context) whenever(context.packageManager).thenReturn(mockPackageManager) preference = CardPreference(context) lifecycleCoroutineScope = TestLifecycleOwner().lifecycleScope whenever(preferenceLifecycleContext.findPreference<Preference>(any())) .thenReturn(preference) whenever(preferenceLifecycleContext.lifecycleScope).thenReturn(lifecycleCoroutineScope) Dispatchers.setMain(testDispatcher) } @After fun tearDown() { Dispatchers.resetMain() private val mockPackageManager: PackageManager = mock() private val preferenceLifecycleContext: PreferenceLifecycleContext = mock { on { lifecycleScope }.thenReturn(testScope) on { packageManager }.thenReturn(mockPackageManager) on { findPreference<Preference>(any()) }.thenReturn(preference) } private val preferenceDataProvider: PreferenceDataProvider = mock { onBlocking { getPreferenceData(any()) } .thenAnswer { when (preferenceData) { null -> mapOf<String, PreferenceData>() else -> mapOf(KEY to preferenceData) } } } @Test Loading @@ -114,121 +89,94 @@ class SupervisionPromoFooterPreferenceTest { @Test fun onResume_actionIsNull_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(targetPackage = "test.package") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(targetPackage = "test.package") promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() verify(mockPackageManager, never()) .queryIntentActivitiesAsUser(any<Intent>(), any<Int>(), any<Int>()) .queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } @Test fun onResume_packageIsNull_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(action = "Test Action") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(action = "Test Action") promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() verify(mockPackageManager, never()) .queryIntentActivitiesAsUser(any<Intent>(), any<Int>(), any<Int>()) .queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } @Test fun onResume_emptyPreferenceData_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred(mapOf<String, PreferenceData>()) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = null promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() verify(mockPackageManager, never()) .queryIntentActivitiesAsUser(any<Intent>(), any<Int>(), any<Int>()) .queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } @Test fun onResume_noActivitiesCanHandleIntent_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(action = "Test Action", targetPackage = "test.package") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(action = "Test Action", targetPackage = "test.package") whenever( mockPackageManager.queryIntentActivitiesAsUser( any<Intent>(), any<Int>(), any<Int>(), ) ) mockPackageManager.stub { on { queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } .thenReturn(emptyList()) } promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() } @Test fun onResume_validIntent_hasActivityToHandleIntent_preferenceIsVisible_validIntentCreated() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(action = "Test Action", targetPackage = "test.package") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(action = "Test Action", targetPackage = "test.package") whenever( mockPackageManager.queryIntentActivitiesAsUser( any<Intent>(), any<Int>(), any<Int>(), ) ) mockPackageManager.stub { on { queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } .thenReturn(listOf(ResolveInfo())) } promoPreference.onResume(preferenceLifecycleContext) assertTrue(preference.isVisible) assertEquals("Test Action", promoPreference.intent(context)?.action) assertEquals("test.package", promoPreference.intent(context)?.`package`) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isTrue() val intent = preference.intent!! assertThat(intent.action).isEqualTo("Test Action") assertThat(intent.`package`).isEqualTo("test.package") } } Loading
src/com/android/settings/supervision/PreferenceDataProvider.kt +1 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.settings.supervision import com.android.settings.supervision.ipc.PreferenceData import kotlinx.coroutines.Deferred /** * Interface for providing preference data. Loading @@ -36,5 +35,5 @@ interface PreferenceDataProvider { * @return A map where the keys are the requested keys, and the values are the corresponding * [PreferenceData] objects. */ suspend fun getPreferenceData(keys: List<String>): Deferred<Map<String, PreferenceData>> suspend fun getPreferenceData(keys: List<String>): Map<String, PreferenceData> }
src/com/android/settings/supervision/SupervisionPromoFooterPreference.kt +40 −31 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.settings.supervision import android.content.Context import android.content.Intent import androidx.preference.Preference import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata Loading @@ -24,19 +25,31 @@ import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.PreferenceTitleProvider import com.android.settingslib.preference.PreferenceBinding import com.android.settingslib.widget.CardPreference import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext /** A bottom banner promoting other supervision features offered by the supervision app. */ class SupervisionPromoFooterPreference(private val preferenceDataProvider: PreferenceDataProvider) : class SupervisionPromoFooterPreference( private val preferenceDataProvider: PreferenceDataProvider, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : PreferenceMetadata, PreferenceBinding, PreferenceLifecycleProvider, PreferenceTitleProvider, PreferenceSummaryProvider { /** Whether [intent] holds an initialized value. */ private var initialized = false // Operation to be performed when the preference is clicked private var intent: Intent? = null override val key: String get() = KEY // TODO(b/399497788): Remove this and get title from supervision app override fun getTitle(context: Context) = "Full parental controls" Loading @@ -44,44 +57,40 @@ class SupervisionPromoFooterPreference(private val preferenceDataProvider: Prefe override fun getSummary(context: Context) = "Set up an account for your kid & help them manage it (required for kids under [AOC])" override fun intent(context: Context): Intent? = intent override val key: String get() = KEY override fun createWidget(context: Context) = CardPreference(context) override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) if (initialized) { preference.intent = intent preference.isVisible = intent != null } } override fun onResume(context: PreferenceLifecycleContext) { super.onResume(context) val preference = context.findPreference<CardPreference>(KEY) if (preference != null) { context.lifecycleScope.launch { // TODO(b/399497788) Get title & summary from supervision app. val preferenceData = preferenceDataProvider.getPreferenceData(listOf(KEY)).await()[KEY] intent = intent ?: Intent() intent?.setAction(preferenceData?.action) intent?.setPackage(preferenceData?.targetPackage) // Hide the preference if the target package can not respond to the action if (!canTargetPackageRespondToAction(preference.context)) { preference.isVisible = false withContext(coroutineDispatcher) { preferenceDataProvider.getPreferenceData(listOf(KEY))[KEY] } initialized = true val targetIntent = Intent(preferenceData?.action).apply { `package` = preferenceData?.targetPackage addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } if (targetIntent.isValid(context)) intent = targetIntent context.notifyPreferenceChange(KEY) } } private fun canTargetPackageRespondToAction(context: Context): Boolean { if (intent?.action == null || intent?.`package` == null) { return false } intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val activities = context.packageManager.queryIntentActivitiesAsUser(intent!!, 0, context.userId) return activities.isNotEmpty() } private fun Intent.isValid(context: Context) = action != null && `package` != null && context.packageManager.queryIntentActivitiesAsUser(this, 0, context.userId).isNotEmpty() companion object { const val KEY = "supervision_promo_footer" const val KEY = "promo_footer" } }
src/com/android/settings/supervision/ipc/SupervisionMessengerClient.kt +5 −7 Original line number Diff line number Diff line Loading @@ -21,8 +21,6 @@ import android.util.Log import com.android.internal.R import com.android.settings.supervision.PreferenceDataProvider import com.android.settingslib.ipc.MessengerServiceClient import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred /** * A specialized [MessengerServiceClient] for interacting with the system supervision service. Loading Loading @@ -55,17 +53,17 @@ class SupervisionMessengerClient(context: Context) : * @return A map of preference data, where the keys are the preference keys and the values are * [PreferenceData] objects, or an empty map if an error occurs. */ override suspend fun getPreferenceData( keys: List<String> ): Deferred<Map<String, PreferenceData>> = override suspend fun getPreferenceData(keys: List<String>): Map<String, PreferenceData> = try { invoke(supervisionPackageName, PreferenceDataApi(), PreferenceDataRequest(keys = keys)) .await() } catch (e: Exception) { Log.e("MessengerService", "Error fetching Preference data from supervision app", e) CompletableDeferred(mapOf()) Log.e(TAG, "Error fetching Preference data from supervision app", e) mapOf() } companion object { private const val TAG = "SupervisionMessengerClient" private const val SUPERVISION_MESSENGER_SERVICE_BIND_ACTION = "android.app.supervision.action.SUPERVISION_MESSENGER_SERVICE" } Loading
tests/robotests/src/com/android/settings/supervision/SupervisionPromoFooterPreferenceTest.kt +68 −120 Original line number Diff line number Diff line Loading @@ -16,80 +16,55 @@ package com.android.settings.supervision import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.lifecycleScope import androidx.lifecycle.testing.TestLifecycleOwner import androidx.preference.Preference import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.android.settings.supervision.SupervisionPromoFooterPreference.Companion.KEY import com.android.settings.supervision.ipc.PreferenceData import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.getPreferenceSummary import com.android.settingslib.metadata.getPreferenceTitle import com.android.settingslib.widget.CardPreference import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @ExperimentalCoroutinesApi @RunWith(AndroidJUnit4::class) class SupervisionPromoFooterPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() private val preference = CardPreference(context) @get:Rule val mocks: MockitoRule = MockitoJUnit.rule() private var preferenceData: PreferenceData? = null private val testDispatcher = UnconfinedTestDispatcher() private val testScope = TestScope(testDispatcher) private lateinit var preference: CardPreference private lateinit var context: Context private lateinit var lifecycleCoroutineScope: LifecycleCoroutineScope @Mock private lateinit var preferenceLifecycleContext: PreferenceLifecycleContext @Mock private lateinit var preferenceDataProvider: PreferenceDataProvider @Mock private lateinit var mockPackageManager: PackageManager @Before fun setup() { context = spy(InstrumentationRegistry.getInstrumentation().context) whenever(context.packageManager).thenReturn(mockPackageManager) preference = CardPreference(context) lifecycleCoroutineScope = TestLifecycleOwner().lifecycleScope whenever(preferenceLifecycleContext.findPreference<Preference>(any())) .thenReturn(preference) whenever(preferenceLifecycleContext.lifecycleScope).thenReturn(lifecycleCoroutineScope) Dispatchers.setMain(testDispatcher) } @After fun tearDown() { Dispatchers.resetMain() private val mockPackageManager: PackageManager = mock() private val preferenceLifecycleContext: PreferenceLifecycleContext = mock { on { lifecycleScope }.thenReturn(testScope) on { packageManager }.thenReturn(mockPackageManager) on { findPreference<Preference>(any()) }.thenReturn(preference) } private val preferenceDataProvider: PreferenceDataProvider = mock { onBlocking { getPreferenceData(any()) } .thenAnswer { when (preferenceData) { null -> mapOf<String, PreferenceData>() else -> mapOf(KEY to preferenceData) } } } @Test Loading @@ -114,121 +89,94 @@ class SupervisionPromoFooterPreferenceTest { @Test fun onResume_actionIsNull_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(targetPackage = "test.package") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(targetPackage = "test.package") promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() verify(mockPackageManager, never()) .queryIntentActivitiesAsUser(any<Intent>(), any<Int>(), any<Int>()) .queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } @Test fun onResume_packageIsNull_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(action = "Test Action") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(action = "Test Action") promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() verify(mockPackageManager, never()) .queryIntentActivitiesAsUser(any<Intent>(), any<Int>(), any<Int>()) .queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } @Test fun onResume_emptyPreferenceData_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred(mapOf<String, PreferenceData>()) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = null promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() verify(mockPackageManager, never()) .queryIntentActivitiesAsUser(any<Intent>(), any<Int>(), any<Int>()) .queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } @Test fun onResume_noActivitiesCanHandleIntent_preferenceIsHidden() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(action = "Test Action", targetPackage = "test.package") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(action = "Test Action", targetPackage = "test.package") whenever( mockPackageManager.queryIntentActivitiesAsUser( any<Intent>(), any<Int>(), any<Int>(), ) ) mockPackageManager.stub { on { queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } .thenReturn(emptyList()) } promoPreference.onResume(preferenceLifecycleContext) assertFalse(preference.isVisible) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isFalse() } @Test fun onResume_validIntent_hasActivityToHandleIntent_preferenceIsVisible_validIntentCreated() = testScope.runTest { val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider) promoPreference.bind(preference, mock()) whenever(preferenceDataProvider.getPreferenceData(any())).thenAnswer { CompletableDeferred( mapOf( SupervisionPromoFooterPreference.KEY to PreferenceData(action = "Test Action", targetPackage = "test.package") ) ) } val promoPreference = SupervisionPromoFooterPreference(preferenceDataProvider, testDispatcher) preferenceData = PreferenceData(action = "Test Action", targetPackage = "test.package") whenever( mockPackageManager.queryIntentActivitiesAsUser( any<Intent>(), any<Int>(), any<Int>(), ) ) mockPackageManager.stub { on { queryIntentActivitiesAsUser(any(), any<Int>(), any<Int>()) } .thenReturn(listOf(ResolveInfo())) } promoPreference.onResume(preferenceLifecycleContext) assertTrue(preference.isVisible) assertEquals("Test Action", promoPreference.intent(context)?.action) assertEquals("test.package", promoPreference.intent(context)?.`package`) verify(preferenceLifecycleContext).notifyPreferenceChange(KEY) // will trigger binding promoPreference.bind(preference, mock()) assertThat(preference.isVisible).isTrue() val intent = preference.intent!! assertThat(intent.action).isEqualTo("Test Action") assertThat(intent.`package`).isEqualTo("test.package") } }