From d29f47f3fe072db18ec440c3045045101bbcb67e Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Fri, 4 Nov 2022 11:39:53 -0700 Subject: [PATCH] Persistence layer for quick affordances. This approach uses SharedPreferences to persist the selected affordance IDs for each slot. Fix: 254858695 Test: unit tests but also manually verified that selecting some affordances and seeing them on the lock screen then killing the system UI process via adb and locking the screen again still showed the selected affordances as expected. Change-Id: If95af61c7beb14ce97e08018ce7c3b5eed6a06d6 --- .../KeyguardQuickAffordanceProvider.kt | 9 +- ...KeyguardQuickAffordanceSelectionManager.kt | 89 +++++-- .../KeyguardQuickAffordanceRepository.kt | 18 +- .../KeyguardQuickAffordanceInteractor.kt | 6 +- .../KeyguardQuickAffordanceProviderTest.kt | 22 +- ...uardQuickAffordanceSelectionManagerTest.kt | 252 +++++++++++++----- .../KeyguardQuickAffordanceRepositoryTest.kt | 24 +- ...ckAffordanceInteractorParameterizedTest.kt | 26 +- .../KeyguardQuickAffordanceInteractorTest.kt | 21 +- .../KeyguardBottomAreaViewModelTest.kt | 20 +- 10 files changed, 365 insertions(+), 122 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt index 0f4581ce3e616..82209637e9fa7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt @@ -31,7 +31,6 @@ import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCall import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract import javax.inject.Inject -import kotlinx.coroutines.runBlocking class KeyguardQuickAffordanceProvider : ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer { @@ -171,12 +170,11 @@ class KeyguardQuickAffordanceProvider : throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!") } - val success = runBlocking { + val success = interactor.select( slotId = slotId, affordanceId = affordanceId, ) - } return if (success) { Log.d(TAG, "Successfully selected $affordanceId for slot $slotId") @@ -196,7 +194,7 @@ class KeyguardQuickAffordanceProvider : ) ) .apply { - val affordanceIdsBySlotId = runBlocking { interactor.getSelections() } + val affordanceIdsBySlotId = interactor.getSelections() affordanceIdsBySlotId.entries.forEach { (slotId, affordanceIds) -> affordanceIds.forEach { affordanceId -> addRow( @@ -271,12 +269,11 @@ class KeyguardQuickAffordanceProvider : ) } - val deleted = runBlocking { + val deleted = interactor.unselect( slotId = slotId, affordanceId = affordanceId, ) - } return if (deleted) { Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt index 9c9354fec6950..d89ac37322b42 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt @@ -17,46 +17,105 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.Context +import android.content.SharedPreferences +import androidx.annotation.VisibleForTesting +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.settings.UserFileManager +import com.android.systemui.settings.UserTracker import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flatMapLatest /** * Manages and provides access to the current "selections" of keyguard quick affordances, answering * the question "which affordances should the keyguard show?". */ @SysUISingleton -class KeyguardQuickAffordanceSelectionManager @Inject constructor() { +class KeyguardQuickAffordanceSelectionManager +@Inject +constructor( + private val userFileManager: UserFileManager, + private val userTracker: UserTracker, +) { - // TODO(b/254858695): implement a persistence layer (database). - private val _selections = MutableStateFlow>>(emptyMap()) + private val sharedPrefs: SharedPreferences + get() = + userFileManager.getSharedPreferences( + FILE_NAME, + Context.MODE_PRIVATE, + userTracker.userId, + ) + + private val userId: Flow = conflatedCallbackFlow { + val callback = + object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + trySendWithFailureLogging(newUser, TAG) + } + } + + userTracker.addCallback(callback) { it.run() } + trySendWithFailureLogging(userTracker.userId, TAG) + + awaitClose { userTracker.removeCallback(callback) } + } /** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */ - val selections: Flow>> = _selections.asStateFlow() + val selections: Flow>> = + userId.flatMapLatest { + conflatedCallbackFlow { + val listener = + SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> + trySend(getSelections()) + } + + sharedPrefs.registerOnSharedPreferenceChangeListener(listener) + send(getSelections()) + + awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) } + } + } /** * Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in * descending priority order. */ - suspend fun getSelections(): Map> { - return _selections.value + fun getSelections(): Map> { + val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) } + return slotKeys.associate { key -> + val slotId = key.substring(KEY_PREFIX_SLOT.length) + val value = sharedPrefs.getString(key, null) + val affordanceIds = + if (!value.isNullOrEmpty()) { + value.split(DELIMITER) + } else { + emptyList() + } + slotId to affordanceIds + } } /** * Updates the IDs of affordances to show at the slot with the given ID. The order of affordance * IDs should be descending priority order. */ - suspend fun setSelections( + fun setSelections( slotId: String, affordanceIds: List, ) { - // Must make a copy of the map and update it, otherwise, the MutableStateFlow won't emit - // when we set its value to the same instance of the original map, even if we change the - // map by updating the value of one of its keys. - val copy = _selections.value.toMutableMap() - copy[slotId] = affordanceIds - _selections.value = copy + val key = "$KEY_PREFIX_SLOT$slotId" + val value = affordanceIds.joinToString(DELIMITER) + sharedPrefs.edit().putString(key, value).apply() + } + + companion object { + private const val TAG = "KeyguardQuickAffordanceSelectionManager" + @VisibleForTesting const val FILE_NAME = "quick_affordance_selections" + private const val KEY_PREFIX_SLOT = "slot_" + private const val DELIMITER = "," } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt index 9fda98c5f7c2c..a1fdbe4a90126 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt @@ -21,19 +21,16 @@ import android.content.Context import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation import javax.inject.Inject -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Abstracts access to application state related to keyguard quick affordances. */ @SysUISingleton @@ -42,7 +39,6 @@ class KeyguardQuickAffordanceRepository constructor( @Application private val appContext: Context, @Application private val scope: CoroutineScope, - @Background private val backgroundDispatcher: CoroutineDispatcher, private val selectionManager: KeyguardQuickAffordanceSelectionManager, private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>, ) { @@ -91,7 +87,7 @@ constructor( * Returns a snapshot of the [KeyguardQuickAffordanceConfig] instances of the affordances at the * slot with the given ID. The configs are sorted in descending priority order. */ - suspend fun getSelections(slotId: String): List { + fun getSelections(slotId: String): List { val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList()) return configs.filter { selections.contains(it.key) } } @@ -100,7 +96,7 @@ constructor( * Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs * are sorted in descending priority order. */ - suspend fun getSelections(): Map> { + fun getSelections(): Map> { return selectionManager.getSelections() } @@ -112,12 +108,10 @@ constructor( slotId: String, affordanceIds: List, ) { - scope.launch(backgroundDispatcher) { - selectionManager.setSelections( - slotId = slotId, - affordanceIds = affordanceIds, - ) - } + selectionManager.setSelections( + slotId = slotId, + affordanceIds = affordanceIds, + ) } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 92caa89bb0e8b..88fed18574cd6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -117,7 +117,7 @@ constructor( * * @return `true` if the affordance was selected successfully; `false` otherwise. */ - suspend fun select(slotId: String, affordanceId: String): Boolean { + fun select(slotId: String, affordanceId: String): Boolean { check(isUsingRepository) val slots = repository.get().getSlotPickerRepresentations() @@ -152,7 +152,7 @@ constructor( * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if * the affordance was not on the slot to begin with). */ - suspend fun unselect(slotId: String, affordanceId: String?): Boolean { + fun unselect(slotId: String, affordanceId: String?): Boolean { check(isUsingRepository) val slots = repository.get().getSlotPickerRepresentations() @@ -187,7 +187,7 @@ constructor( } /** Returns affordance IDs indexed by slot ID, for all known slots. */ - suspend fun getSelections(): Map> { + fun getSelections(): Map> { check(isUsingRepository) val selections = repository.get().getSelections() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt index ed08849fe70c0..2d0f9e264f692 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt @@ -33,11 +33,14 @@ import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepo import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -46,6 +49,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -70,8 +75,21 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() { KeyguardQuickAffordanceRepository( appContext = context, scope = CoroutineScope(IMMEDIATE), - backgroundDispatcher = IMMEDIATE, - selectionManager = KeyguardQuickAffordanceSelectionManager(), + selectionManager = + KeyguardQuickAffordanceSelectionManager( + userFileManager = + mock().apply { + whenever( + getSharedPreferences( + anyString(), + anyInt(), + anyInt(), + ) + ) + .thenReturn(FakeSharedPreferences()) + }, + userTracker = userTracker, + ), configs = setOf( FakeKeyguardQuickAffordanceConfig( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt index d2422ad7b53b3..b8bef1a9d6f36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt @@ -17,111 +17,215 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.SharedPreferences +import android.content.pm.UserInfo +import androidx.test.core.app.ActivityScenario.launch import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.UserFileManager +import com.android.systemui.util.FakeSharedPreferences +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.MockitoAnnotations @SmallTest @RunWith(JUnit4::class) class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() { + @Mock private lateinit var userFileManager: UserFileManager + private lateinit var underTest: KeyguardQuickAffordanceSelectionManager + private lateinit var userTracker: FakeUserTracker + private lateinit var sharedPrefs: MutableMap + @Before fun setUp() { - underTest = KeyguardQuickAffordanceSelectionManager() + MockitoAnnotations.initMocks(this) + sharedPrefs = mutableMapOf() + whenever(userFileManager.getSharedPreferences(anyString(), anyInt(), anyInt())).thenAnswer { + val userId = it.arguments[2] as Int + sharedPrefs.getOrPut(userId) { FakeSharedPreferences() } + } + userTracker = FakeUserTracker() + + underTest = + KeyguardQuickAffordanceSelectionManager( + userFileManager = userFileManager, + userTracker = userTracker, + ) } @Test - fun setSelections() = - runBlocking(IMMEDIATE) { - var affordanceIdsBySlotId: Map>? = null - val job = underTest.selections.onEach { affordanceIdsBySlotId = it }.launchIn(this) - val slotId1 = "slot1" - val slotId2 = "slot2" - val affordanceId1 = "affordance1" - val affordanceId2 = "affordance2" - val affordanceId3 = "affordance3" + fun setSelections() = runTest { + val affordanceIdsBySlotId = mutableListOf>>() + val job = + launch(UnconfinedTestDispatcher()) { + underTest.selections.toList(affordanceIdsBySlotId) + } + val slotId1 = "slot1" + val slotId2 = "slot2" + val affordanceId1 = "affordance1" + val affordanceId2 = "affordance2" + val affordanceId3 = "affordance3" - underTest.setSelections( - slotId = slotId1, - affordanceIds = listOf(affordanceId1), + underTest.setSelections( + slotId = slotId1, + affordanceIds = listOf(affordanceId1), + ) + assertSelections( + affordanceIdsBySlotId.last(), + mapOf( + slotId1 to listOf(affordanceId1), + ), + ) + + underTest.setSelections( + slotId = slotId2, + affordanceIds = listOf(affordanceId2), + ) + assertSelections( + affordanceIdsBySlotId.last(), + mapOf( + slotId1 to listOf(affordanceId1), + slotId2 to listOf(affordanceId2), ) - assertSelections( - affordanceIdsBySlotId, + ) + + underTest.setSelections( + slotId = slotId1, + affordanceIds = listOf(affordanceId1, affordanceId3), + ) + assertSelections( + affordanceIdsBySlotId.last(), + mapOf( + slotId1 to listOf(affordanceId1, affordanceId3), + slotId2 to listOf(affordanceId2), + ) + ) + + underTest.setSelections( + slotId = slotId1, + affordanceIds = listOf(affordanceId3), + ) + assertSelections( + affordanceIdsBySlotId.last(), + mapOf( + slotId1 to listOf(affordanceId3), + slotId2 to listOf(affordanceId2), + ) + ) + + underTest.setSelections( + slotId = slotId2, + affordanceIds = listOf(), + ) + assertSelections( + affordanceIdsBySlotId.last(), + mapOf( + slotId1 to listOf(affordanceId3), + slotId2 to listOf(), + ) + ) + + job.cancel() + } + + @Test + fun `remembers selections by user`() = runTest { + val slot1 = "slot_1" + val slot2 = "slot_2" + val affordance1 = "affordance_1" + val affordance2 = "affordance_2" + val affordance3 = "affordance_3" + + val affordanceIdsBySlotId = mutableListOf>>() + val job = + launch(UnconfinedTestDispatcher()) { + underTest.selections.toList(affordanceIdsBySlotId) + } + + val userInfos = + listOf( + UserInfo(/* id= */ 0, "zero", /* flags= */ 0), + UserInfo(/* id= */ 1, "one", /* flags= */ 0), + ) + userTracker.set( + userInfos = userInfos, + selectedUserIndex = 0, + ) + underTest.setSelections( + slotId = slot1, + affordanceIds = listOf(affordance1), + ) + underTest.setSelections( + slotId = slot2, + affordanceIds = listOf(affordance2), + ) + + // Switch to user 1 + userTracker.set( + userInfos = userInfos, + selectedUserIndex = 1, + ) + // We never set selections on user 1, so it should be empty. + assertSelections( + observed = affordanceIdsBySlotId.last(), + expected = emptyMap(), + ) + // Now, let's set selections on user 1. + underTest.setSelections( + slotId = slot1, + affordanceIds = listOf(affordance2), + ) + underTest.setSelections( + slotId = slot2, + affordanceIds = listOf(affordance3), + ) + assertSelections( + observed = affordanceIdsBySlotId.last(), + expected = mapOf( - slotId1 to listOf(affordanceId1), + slot1 to listOf(affordance2), + slot2 to listOf(affordance3), ), - ) + ) - underTest.setSelections( - slotId = slotId2, - affordanceIds = listOf(affordanceId2), - ) - assertSelections( - affordanceIdsBySlotId, + // Switch back to user 0. + userTracker.set( + userInfos = userInfos, + selectedUserIndex = 0, + ) + // Assert that we still remember the old selections for user 0. + assertSelections( + observed = affordanceIdsBySlotId.last(), + expected = mapOf( - slotId1 to listOf(affordanceId1), - slotId2 to listOf(affordanceId2), - ) - ) + slot1 to listOf(affordance1), + slot2 to listOf(affordance2), + ), + ) - underTest.setSelections( - slotId = slotId1, - affordanceIds = listOf(affordanceId1, affordanceId3), - ) - assertSelections( - affordanceIdsBySlotId, - mapOf( - slotId1 to listOf(affordanceId1, affordanceId3), - slotId2 to listOf(affordanceId2), - ) - ) + job.cancel() + } - underTest.setSelections( - slotId = slotId1, - affordanceIds = listOf(affordanceId3), - ) - assertSelections( - affordanceIdsBySlotId, - mapOf( - slotId1 to listOf(affordanceId3), - slotId2 to listOf(affordanceId2), - ) - ) - - underTest.setSelections( - slotId = slotId2, - affordanceIds = listOf(), - ) - assertSelections( - affordanceIdsBySlotId, - mapOf( - slotId1 to listOf(affordanceId3), - slotId2 to listOf(), - ) - ) - - job.cancel() - } - - private suspend fun assertSelections( + private fun assertSelections( observed: Map>?, expected: Map>, ) { assertThat(underTest.getSelections()).isEqualTo(expected) assertThat(observed).isEqualTo(expected) } - - companion object { - private val IMMEDIATE = Dispatchers.Main.immediate - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index dceb492af2ed0..a95f788b1ee91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -25,6 +25,11 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.UserFileManager +import com.android.systemui.util.FakeSharedPreferences +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -36,6 +41,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -55,8 +62,21 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { KeyguardQuickAffordanceRepository( appContext = context, scope = CoroutineScope(IMMEDIATE), - backgroundDispatcher = IMMEDIATE, - selectionManager = KeyguardQuickAffordanceSelectionManager(), + selectionManager = + KeyguardQuickAffordanceSelectionManager( + userFileManager = + mock().apply { + whenever( + getSharedPreferences( + anyString(), + anyInt(), + anyInt(), + ) + ) + .thenReturn(FakeSharedPreferences()) + }, + userTracker = FakeUserTracker(), + ), configs = setOf(config1, config2), ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 737f242247273..3e37459d8eeb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -36,8 +36,11 @@ import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepo import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -50,6 +53,8 @@ import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.junit.runners.Parameterized.Parameter import org.junit.runners.Parameterized.Parameters +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.ArgumentMatchers.eq import org.mockito.ArgumentMatchers.same import org.mockito.Mock @@ -201,7 +206,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var keyguardStateController: KeyguardStateController - @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller @Mock private lateinit var expandable: Expandable @@ -214,12 +218,14 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false @JvmField @Parameter(4) var startActivity: Boolean = false private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig + private lateinit var userTracker: UserTracker @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(expandable.activityLaunchController()).thenReturn(animationController) + userTracker = FakeUserTracker() homeControls = FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) val quickAccessWallet = @@ -232,8 +238,21 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { KeyguardQuickAffordanceRepository( appContext = context, scope = CoroutineScope(IMMEDIATE), - backgroundDispatcher = IMMEDIATE, - selectionManager = KeyguardQuickAffordanceSelectionManager(), + selectionManager = + KeyguardQuickAffordanceSelectionManager( + userFileManager = + mock().apply { + whenever( + getSharedPreferences( + anyString(), + anyInt(), + anyInt(), + ) + ) + .thenReturn(FakeSharedPreferences()) + }, + userTracker = userTracker, + ), configs = setOf(homeControls, quickAccessWallet, qrCodeScanner), ) underTest = @@ -319,7 +338,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { needStrongAuthAfterBoot: Boolean = true, keyguardIsUnlocked: Boolean = false, ) { - whenever(userTracker.userHandle).thenReturn(mock()) whenever(lockPatternUtils.getStrongAuthForUser(any())) .thenReturn( if (needStrongAuthAfterBoot) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index ffcf832b95aec..f887bf93de8d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -35,9 +35,11 @@ import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAff import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -53,6 +55,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.MockitoAnnotations @@ -94,8 +98,21 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { KeyguardQuickAffordanceRepository( appContext = context, scope = CoroutineScope(IMMEDIATE), - backgroundDispatcher = IMMEDIATE, - selectionManager = KeyguardQuickAffordanceSelectionManager(), + selectionManager = + KeyguardQuickAffordanceSelectionManager( + userFileManager = + mock().apply { + whenever( + getSharedPreferences( + anyString(), + anyInt(), + anyInt(), + ) + ) + .thenReturn(FakeSharedPreferences()) + }, + userTracker = userTracker, + ), configs = setOf(homeControls, quickAccessWallet, qrCodeScanner), ) featureFlags = diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index fa2ac46f46d20..03e6ccb6fabe2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -38,8 +38,10 @@ import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAff import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat @@ -56,6 +58,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.verifyZeroInteractions @@ -119,8 +122,21 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { KeyguardQuickAffordanceRepository( appContext = context, scope = CoroutineScope(IMMEDIATE), - backgroundDispatcher = IMMEDIATE, - selectionManager = KeyguardQuickAffordanceSelectionManager(), + selectionManager = + KeyguardQuickAffordanceSelectionManager( + userFileManager = + mock().apply { + whenever( + getSharedPreferences( + anyString(), + anyInt(), + anyInt(), + ) + ) + .thenReturn(FakeSharedPreferences()) + }, + userTracker = userTracker, + ), configs = setOf( homeControlsQuickAffordanceConfig,