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
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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<Map<String, List<String>>>(emptyMap())
|
||||
private val sharedPrefs: SharedPreferences
|
||||
get() =
|
||||
userFileManager.getSharedPreferences(
|
||||
FILE_NAME,
|
||||
Context.MODE_PRIVATE,
|
||||
userTracker.userId,
|
||||
)
|
||||
|
||||
private val userId: Flow<Int> = 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<Map<String, List<String>>> = _selections.asStateFlow()
|
||||
val selections: Flow<Map<String, List<String>>> =
|
||||
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<String, List<String>> {
|
||||
return _selections.value
|
||||
fun getSelections(): Map<String, List<String>> {
|
||||
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<String>,
|
||||
) {
|
||||
// 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 = ","
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<KeyguardQuickAffordanceConfig> {
|
||||
fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
|
||||
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<String, List<String>> {
|
||||
fun getSelections(): Map<String, List<String>> {
|
||||
return selectionManager.getSelections()
|
||||
}
|
||||
|
||||
@@ -112,12 +108,10 @@ constructor(
|
||||
slotId: String,
|
||||
affordanceIds: List<String>,
|
||||
) {
|
||||
scope.launch(backgroundDispatcher) {
|
||||
selectionManager.setSelections(
|
||||
slotId = slotId,
|
||||
affordanceIds = affordanceIds,
|
||||
)
|
||||
}
|
||||
selectionManager.setSelections(
|
||||
slotId = slotId,
|
||||
affordanceIds = affordanceIds,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<String, List<String>> {
|
||||
fun getSelections(): Map<String, List<String>> {
|
||||
check(isUsingRepository)
|
||||
|
||||
val selections = repository.get().getSelections()
|
||||
|
||||
@@ -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<UserFileManager>().apply {
|
||||
whenever(
|
||||
getSharedPreferences(
|
||||
anyString(),
|
||||
anyInt(),
|
||||
anyInt(),
|
||||
)
|
||||
)
|
||||
.thenReturn(FakeSharedPreferences())
|
||||
},
|
||||
userTracker = userTracker,
|
||||
),
|
||||
configs =
|
||||
setOf(
|
||||
FakeKeyguardQuickAffordanceConfig(
|
||||
|
||||
@@ -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<Int, SharedPreferences>
|
||||
|
||||
@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<String, List<String>>? = 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<Map<String, List<String>>>()
|
||||
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<Map<String, List<String>>>()
|
||||
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<String, List<String>>?,
|
||||
expected: Map<String, List<String>>,
|
||||
) {
|
||||
assertThat(underTest.getSelections()).isEqualTo(expected)
|
||||
assertThat(observed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UserFileManager>().apply {
|
||||
whenever(
|
||||
getSharedPreferences(
|
||||
anyString(),
|
||||
anyInt(),
|
||||
anyInt(),
|
||||
)
|
||||
)
|
||||
.thenReturn(FakeSharedPreferences())
|
||||
},
|
||||
userTracker = FakeUserTracker(),
|
||||
),
|
||||
configs = setOf(config1, config2),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<UserFileManager>().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) {
|
||||
|
||||
@@ -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<UserFileManager>().apply {
|
||||
whenever(
|
||||
getSharedPreferences(
|
||||
anyString(),
|
||||
anyInt(),
|
||||
anyInt(),
|
||||
)
|
||||
)
|
||||
.thenReturn(FakeSharedPreferences())
|
||||
},
|
||||
userTracker = userTracker,
|
||||
),
|
||||
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||
)
|
||||
featureFlags =
|
||||
|
||||
@@ -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<UserFileManager>().apply {
|
||||
whenever(
|
||||
getSharedPreferences(
|
||||
anyString(),
|
||||
anyInt(),
|
||||
anyInt(),
|
||||
)
|
||||
)
|
||||
.thenReturn(FakeSharedPreferences())
|
||||
},
|
||||
userTracker = userTracker,
|
||||
),
|
||||
configs =
|
||||
setOf(
|
||||
homeControlsQuickAffordanceConfig,
|
||||
|
||||
Reference in New Issue
Block a user