Merge changes I2ea2baa4,Icd8ff155,I63484a92,If95af61c into tm-qpr-dev
* changes: Exposes feature flag to wallpaper picker. Legacy setting syncer. Defines config for default affordances. Persistence layer for quick affordances.
This commit is contained in:
@@ -817,4 +817,13 @@
|
|||||||
<item>bottom_end:1</item>
|
<item>bottom_end:1</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
|
||||||
|
string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
|
||||||
|
separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
|
||||||
|
default is displayed by System UI as long as the user hasn't made a different choice for that
|
||||||
|
slot. If the user did make a choice, even if the choice is the "None" option, the default is
|
||||||
|
ignored. -->
|
||||||
|
<string-array name="config_keyguardQuickAffordanceDefaults" translatable="false">
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -108,4 +108,30 @@ object KeyguardQuickAffordanceProviderContract {
|
|||||||
const val AFFORDANCE_ID = "affordance_id"
|
const val AFFORDANCE_ID = "affordance_id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table for flags.
|
||||||
|
*
|
||||||
|
* Flags are key-value pairs.
|
||||||
|
*
|
||||||
|
* Supported operations:
|
||||||
|
* - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
|
||||||
|
* contain rows, each of which with the columns from [FlagsTable.Columns].
|
||||||
|
*/
|
||||||
|
object FlagsTable {
|
||||||
|
const val TABLE_NAME = "flags"
|
||||||
|
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag denoting whether the customizable lock screen quick affordances feature is enabled.
|
||||||
|
*/
|
||||||
|
const val FLAG_NAME_FEATURE_ENABLED = "is_feature_enabled"
|
||||||
|
|
||||||
|
object Columns {
|
||||||
|
/** String. Unique ID for the flag. */
|
||||||
|
const val NAME = "name"
|
||||||
|
/** Int. Value of the flag. `1` means `true` and `0` means `false`. */
|
||||||
|
const val VALUE = "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCall
|
|||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
||||||
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
|
|
||||||
class KeyguardQuickAffordanceProvider :
|
class KeyguardQuickAffordanceProvider :
|
||||||
ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
|
ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
|
||||||
@@ -57,6 +56,11 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
Contract.SelectionTable.TABLE_NAME,
|
Contract.SelectionTable.TABLE_NAME,
|
||||||
MATCH_CODE_ALL_SELECTIONS,
|
MATCH_CODE_ALL_SELECTIONS,
|
||||||
)
|
)
|
||||||
|
addURI(
|
||||||
|
Contract.AUTHORITY,
|
||||||
|
Contract.FlagsTable.TABLE_NAME,
|
||||||
|
MATCH_CODE_ALL_FLAGS,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(): Boolean {
|
override fun onCreate(): Boolean {
|
||||||
@@ -77,6 +81,7 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
when (uriMatcher.match(uri)) {
|
when (uriMatcher.match(uri)) {
|
||||||
MATCH_CODE_ALL_SLOTS,
|
MATCH_CODE_ALL_SLOTS,
|
||||||
MATCH_CODE_ALL_AFFORDANCES,
|
MATCH_CODE_ALL_AFFORDANCES,
|
||||||
|
MATCH_CODE_ALL_FLAGS,
|
||||||
MATCH_CODE_ALL_SELECTIONS -> "vnd.android.cursor.dir/vnd."
|
MATCH_CODE_ALL_SELECTIONS -> "vnd.android.cursor.dir/vnd."
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -86,6 +91,7 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
|
MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
|
||||||
MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
|
MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
|
||||||
MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
|
MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
|
||||||
|
MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +121,7 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
|
MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
|
||||||
MATCH_CODE_ALL_SLOTS -> querySlots()
|
MATCH_CODE_ALL_SLOTS -> querySlots()
|
||||||
MATCH_CODE_ALL_SELECTIONS -> querySelections()
|
MATCH_CODE_ALL_SELECTIONS -> querySelections()
|
||||||
|
MATCH_CODE_ALL_FLAGS -> queryFlags()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,12 +178,11 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!")
|
throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!")
|
||||||
}
|
}
|
||||||
|
|
||||||
val success = runBlocking {
|
val success =
|
||||||
interactor.select(
|
interactor.select(
|
||||||
slotId = slotId,
|
slotId = slotId,
|
||||||
affordanceId = affordanceId,
|
affordanceId = affordanceId,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return if (success) {
|
return if (success) {
|
||||||
Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
|
Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
|
||||||
@@ -196,7 +202,7 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.apply {
|
.apply {
|
||||||
val affordanceIdsBySlotId = runBlocking { interactor.getSelections() }
|
val affordanceIdsBySlotId = interactor.getSelections()
|
||||||
affordanceIdsBySlotId.entries.forEach { (slotId, affordanceIds) ->
|
affordanceIdsBySlotId.entries.forEach { (slotId, affordanceIds) ->
|
||||||
affordanceIds.forEach { affordanceId ->
|
affordanceIds.forEach { affordanceId ->
|
||||||
addRow(
|
addRow(
|
||||||
@@ -250,6 +256,29 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun queryFlags(): Cursor {
|
||||||
|
return MatrixCursor(
|
||||||
|
arrayOf(
|
||||||
|
Contract.FlagsTable.Columns.NAME,
|
||||||
|
Contract.FlagsTable.Columns.VALUE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.apply {
|
||||||
|
interactor.getPickerFlags().forEach { flag ->
|
||||||
|
addRow(
|
||||||
|
arrayOf(
|
||||||
|
flag.name,
|
||||||
|
if (flag.value) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun deleteSelection(
|
private fun deleteSelection(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
selectionArgs: Array<out String>?,
|
selectionArgs: Array<out String>?,
|
||||||
@@ -271,12 +300,11 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val deleted = runBlocking {
|
val deleted =
|
||||||
interactor.unselect(
|
interactor.unselect(
|
||||||
slotId = slotId,
|
slotId = slotId,
|
||||||
affordanceId = affordanceId,
|
affordanceId = affordanceId,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return if (deleted) {
|
return if (deleted) {
|
||||||
Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId")
|
Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId")
|
||||||
@@ -293,5 +321,6 @@ class KeyguardQuickAffordanceProvider :
|
|||||||
private const val MATCH_CODE_ALL_SLOTS = 1
|
private const val MATCH_CODE_ALL_SLOTS = 1
|
||||||
private const val MATCH_CODE_ALL_AFFORDANCES = 2
|
private const val MATCH_CODE_ALL_AFFORDANCES = 2
|
||||||
private const val MATCH_CODE_ALL_SELECTIONS = 3
|
private const val MATCH_CODE_ALL_SELECTIONS = 3
|
||||||
|
private const val MATCH_CODE_ALL_FLAGS = 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.keyguard.data.quickaffordance
|
||||||
|
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.provider.Settings
|
||||||
|
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.KeyguardQuickAffordanceLegacySettingSyncer.Companion.BINDINGS
|
||||||
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
|
import com.android.systemui.util.settings.SecureSettings
|
||||||
|
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps quick affordance selections and legacy user settings in sync.
|
||||||
|
*
|
||||||
|
* "Legacy user settings" are user settings like: Settings > Display > Lock screen > "Show device
|
||||||
|
* controls" Settings > Display > Lock screen > "Show wallet"
|
||||||
|
*
|
||||||
|
* Quick affordance selections are the ones available through the new custom lock screen experience
|
||||||
|
* from Settings > Wallpaper & Style.
|
||||||
|
*
|
||||||
|
* This class keeps these in sync, mostly for backwards compatibility purposes and in order to not
|
||||||
|
* "forget" an existing legacy user setting when the device gets updated with a version of System UI
|
||||||
|
* that has the new customizable lock screen feature.
|
||||||
|
*
|
||||||
|
* The way it works is that, when [startSyncing] is called, the syncer starts coroutines to listen
|
||||||
|
* for changes in both legacy user settings and their respective affordance selections. Whenever one
|
||||||
|
* of each pair is changed, the other member of that pair is also updated to match. For example, if
|
||||||
|
* the user turns on "Show device controls", we automatically select the home controls affordance
|
||||||
|
* for the preferred slot. Conversely, when the home controls affordance is unselected by the user,
|
||||||
|
* we set the "Show device controls" setting to "off".
|
||||||
|
*
|
||||||
|
* The class can be configured by updating its list of triplets in the code under [BINDINGS].
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@Application private val scope: CoroutineScope,
|
||||||
|
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||||
|
private val secureSettings: SecureSettings,
|
||||||
|
private val selectionsManager: KeyguardQuickAffordanceSelectionManager,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private val BINDINGS =
|
||||||
|
listOf(
|
||||||
|
Binding(
|
||||||
|
settingsKey = Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
|
||||||
|
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||||
|
affordanceId = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
|
||||||
|
),
|
||||||
|
Binding(
|
||||||
|
settingsKey = Settings.Secure.LOCKSCREEN_SHOW_WALLET,
|
||||||
|
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
affordanceId = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
|
||||||
|
),
|
||||||
|
Binding(
|
||||||
|
settingsKey = Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER,
|
||||||
|
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
affordanceId = BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startSyncing(
|
||||||
|
bindings: List<Binding> = BINDINGS,
|
||||||
|
): Job {
|
||||||
|
return scope.launch { bindings.forEach { binding -> startSyncing(this, binding) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startSyncing(
|
||||||
|
scope: CoroutineScope,
|
||||||
|
binding: Binding,
|
||||||
|
) {
|
||||||
|
secureSettings
|
||||||
|
.observerFlow(
|
||||||
|
names = arrayOf(binding.settingsKey),
|
||||||
|
userId = UserHandle.USER_ALL,
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
isSet(
|
||||||
|
settingsKey = binding.settingsKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.onEach { isSet ->
|
||||||
|
if (isSelected(binding.affordanceId) != isSet) {
|
||||||
|
if (isSet) {
|
||||||
|
select(
|
||||||
|
slotId = binding.slotId,
|
||||||
|
affordanceId = binding.affordanceId,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
unselect(
|
||||||
|
affordanceId = binding.affordanceId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flowOn(backgroundDispatcher)
|
||||||
|
.launchIn(scope)
|
||||||
|
|
||||||
|
selectionsManager.selections
|
||||||
|
.map { it.values.flatten().toSet() }
|
||||||
|
.map { it.contains(binding.affordanceId) }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.onEach { isSelected ->
|
||||||
|
if (isSet(binding.settingsKey) != isSelected) {
|
||||||
|
set(binding.settingsKey, isSelected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flowOn(backgroundDispatcher)
|
||||||
|
.launchIn(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSelected(
|
||||||
|
affordanceId: String,
|
||||||
|
): Boolean {
|
||||||
|
return selectionsManager
|
||||||
|
.getSelections() // Map<String, List<String>>
|
||||||
|
.values // Collection<List<String>>
|
||||||
|
.flatten() // List<String>
|
||||||
|
.toSet() // Set<String>
|
||||||
|
.contains(affordanceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun select(
|
||||||
|
slotId: String,
|
||||||
|
affordanceId: String,
|
||||||
|
) {
|
||||||
|
val affordanceIdsAtSlotId = selectionsManager.getSelections()[slotId] ?: emptyList()
|
||||||
|
selectionsManager.setSelections(
|
||||||
|
slotId = slotId,
|
||||||
|
affordanceIds = affordanceIdsAtSlotId + listOf(affordanceId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unselect(
|
||||||
|
affordanceId: String,
|
||||||
|
) {
|
||||||
|
val currentSelections = selectionsManager.getSelections()
|
||||||
|
val slotIdsContainingAffordanceId =
|
||||||
|
currentSelections
|
||||||
|
.filter { (_, affordanceIds) -> affordanceIds.contains(affordanceId) }
|
||||||
|
.map { (slotId, _) -> slotId }
|
||||||
|
|
||||||
|
slotIdsContainingAffordanceId.forEach { slotId ->
|
||||||
|
val currentAffordanceIds = currentSelections[slotId] ?: emptyList()
|
||||||
|
val affordanceIdsAfterUnselecting =
|
||||||
|
currentAffordanceIds.toMutableList().apply { remove(affordanceId) }
|
||||||
|
|
||||||
|
selectionsManager.setSelections(
|
||||||
|
slotId = slotId,
|
||||||
|
affordanceIds = affordanceIdsAfterUnselecting,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSet(
|
||||||
|
settingsKey: String,
|
||||||
|
): Boolean {
|
||||||
|
return secureSettings.getIntForUser(
|
||||||
|
settingsKey,
|
||||||
|
0,
|
||||||
|
UserHandle.USER_CURRENT,
|
||||||
|
) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun set(
|
||||||
|
settingsKey: String,
|
||||||
|
isSet: Boolean,
|
||||||
|
) {
|
||||||
|
withContext(backgroundDispatcher) {
|
||||||
|
secureSettings.putInt(
|
||||||
|
settingsKey,
|
||||||
|
if (isSet) 1 else 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Binding(
|
||||||
|
val settingsKey: String,
|
||||||
|
val slotId: String,
|
||||||
|
val affordanceId: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -17,46 +17,138 @@
|
|||||||
|
|
||||||
package com.android.systemui.keyguard.data.quickaffordance
|
package com.android.systemui.keyguard.data.quickaffordance
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.android.systemui.R
|
||||||
|
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.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
|
import com.android.systemui.settings.UserFileManager
|
||||||
|
import com.android.systemui.settings.UserTracker
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages and provides access to the current "selections" of keyguard quick affordances, answering
|
* Manages and provides access to the current "selections" of keyguard quick affordances, answering
|
||||||
* the question "which affordances should the keyguard show?".
|
* the question "which affordances should the keyguard show?".
|
||||||
*/
|
*/
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
class KeyguardQuickAffordanceSelectionManager @Inject constructor() {
|
class KeyguardQuickAffordanceSelectionManager
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@Application context: Context,
|
||||||
|
private val userFileManager: UserFileManager,
|
||||||
|
private val userTracker: UserTracker,
|
||||||
|
) {
|
||||||
|
|
||||||
// TODO(b/254858695): implement a persistence layer (database).
|
private val sharedPrefs: SharedPreferences
|
||||||
private val _selections = MutableStateFlow<Map<String, List<String>>>(emptyMap())
|
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) }
|
||||||
|
}
|
||||||
|
private val defaults: Map<String, List<String>> by lazy {
|
||||||
|
context.resources
|
||||||
|
.getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
|
||||||
|
.associate { item ->
|
||||||
|
val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
|
||||||
|
check(splitUp.size == 2)
|
||||||
|
val slotId = splitUp[0]
|
||||||
|
val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
|
||||||
|
slotId to affordanceIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */
|
/** 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
|
* Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in
|
||||||
* descending priority order.
|
* descending priority order.
|
||||||
*/
|
*/
|
||||||
suspend fun getSelections(): Map<String, List<String>> {
|
fun getSelections(): Map<String, List<String>> {
|
||||||
return _selections.value
|
val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
|
||||||
|
val result =
|
||||||
|
slotKeys
|
||||||
|
.associate { key ->
|
||||||
|
val slotId = key.substring(KEY_PREFIX_SLOT.length)
|
||||||
|
val value = sharedPrefs.getString(key, null)
|
||||||
|
val affordanceIds =
|
||||||
|
if (!value.isNullOrEmpty()) {
|
||||||
|
value.split(AFFORDANCE_DELIMITER)
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
slotId to affordanceIds
|
||||||
|
}
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
|
// If the result map is missing keys, it means that the system has never set anything for
|
||||||
|
// those slots. This is where we need examine our defaults and see if there should be a
|
||||||
|
// default value for the affordances in the slot IDs that are missing from the result.
|
||||||
|
//
|
||||||
|
// Once the user makes any selection for a slot, even when they select "None", this class
|
||||||
|
// will persist a key for that slot ID. In the case of "None", it will have a value of the
|
||||||
|
// empty string. This is why this system works.
|
||||||
|
defaults.forEach { (slotId, affordanceIds) ->
|
||||||
|
if (!result.containsKey(slotId)) {
|
||||||
|
result[slotId] = affordanceIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
|
* Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
|
||||||
* IDs should be descending priority order.
|
* IDs should be descending priority order.
|
||||||
*/
|
*/
|
||||||
suspend fun setSelections(
|
fun setSelections(
|
||||||
slotId: String,
|
slotId: String,
|
||||||
affordanceIds: List<String>,
|
affordanceIds: List<String>,
|
||||||
) {
|
) {
|
||||||
// Must make a copy of the map and update it, otherwise, the MutableStateFlow won't emit
|
val key = "$KEY_PREFIX_SLOT$slotId"
|
||||||
// when we set its value to the same instance of the original map, even if we change the
|
val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
|
||||||
// map by updating the value of one of its keys.
|
sharedPrefs.edit().putString(key, value).apply()
|
||||||
val copy = _selections.value.toMutableMap()
|
}
|
||||||
copy[slotId] = affordanceIds
|
|
||||||
_selections.value = copy
|
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 SLOT_AFFORDANCES_DELIMITER = ":"
|
||||||
|
private const val AFFORDANCE_DELIMITER = ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,19 +21,17 @@ import android.content.Context
|
|||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
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.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
|
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
/** Abstracts access to application state related to keyguard quick affordances. */
|
/** Abstracts access to application state related to keyguard quick affordances. */
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@@ -42,8 +40,8 @@ class KeyguardQuickAffordanceRepository
|
|||||||
constructor(
|
constructor(
|
||||||
@Application private val appContext: Context,
|
@Application private val appContext: Context,
|
||||||
@Application private val scope: CoroutineScope,
|
@Application private val scope: CoroutineScope,
|
||||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
|
||||||
private val selectionManager: KeyguardQuickAffordanceSelectionManager,
|
private val selectionManager: KeyguardQuickAffordanceSelectionManager,
|
||||||
|
legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer,
|
||||||
private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
|
private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
@@ -87,11 +85,15 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
legacySettingSyncer.startSyncing()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a snapshot of the [KeyguardQuickAffordanceConfig] instances of the affordances at the
|
* 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.
|
* 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())
|
val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList())
|
||||||
return configs.filter { selections.contains(it.key) }
|
return configs.filter { selections.contains(it.key) }
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,7 @@ constructor(
|
|||||||
* Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs
|
* Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs
|
||||||
* are sorted in descending priority order.
|
* are sorted in descending priority order.
|
||||||
*/
|
*/
|
||||||
suspend fun getSelections(): Map<String, List<String>> {
|
fun getSelections(): Map<String, List<String>> {
|
||||||
return selectionManager.getSelections()
|
return selectionManager.getSelections()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,13 +114,11 @@ constructor(
|
|||||||
slotId: String,
|
slotId: String,
|
||||||
affordanceIds: List<String>,
|
affordanceIds: List<String>,
|
||||||
) {
|
) {
|
||||||
scope.launch(backgroundDispatcher) {
|
|
||||||
selectionManager.setSelections(
|
selectionManager.setSelections(
|
||||||
slotId = slotId,
|
slotId = slotId,
|
||||||
affordanceIds = affordanceIds,
|
affordanceIds = affordanceIds,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of representation objects for all known affordances, regardless of what is
|
* Returns the list of representation objects for all known affordances, regardless of what is
|
||||||
|
|||||||
@@ -28,11 +28,13 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
|
|||||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
||||||
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
|
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
|
||||||
|
import com.android.systemui.keyguard.shared.model.KeyguardPickerFlag
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
|
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
|
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract
|
||||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
@@ -117,7 +119,7 @@ constructor(
|
|||||||
*
|
*
|
||||||
* @return `true` if the affordance was selected successfully; `false` otherwise.
|
* @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)
|
check(isUsingRepository)
|
||||||
|
|
||||||
val slots = repository.get().getSlotPickerRepresentations()
|
val slots = repository.get().getSlotPickerRepresentations()
|
||||||
@@ -152,7 +154,7 @@ constructor(
|
|||||||
* @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
|
* @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
|
||||||
* the affordance was not on the slot to begin with).
|
* 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)
|
check(isUsingRepository)
|
||||||
|
|
||||||
val slots = repository.get().getSlotPickerRepresentations()
|
val slots = repository.get().getSlotPickerRepresentations()
|
||||||
@@ -187,7 +189,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns affordance IDs indexed by slot ID, for all known slots. */
|
/** 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)
|
check(isUsingRepository)
|
||||||
|
|
||||||
val selections = repository.get().getSelections()
|
val selections = repository.get().getSelections()
|
||||||
@@ -314,6 +316,15 @@ constructor(
|
|||||||
return repository.get().getSlotPickerRepresentations()
|
return repository.get().getSlotPickerRepresentations()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPickerFlags(): List<KeyguardPickerFlag> {
|
||||||
|
return listOf(
|
||||||
|
KeyguardPickerFlag(
|
||||||
|
name = KeyguardQuickAffordanceProviderContract.FlagsTable.FLAG_NAME_FEATURE_ENABLED,
|
||||||
|
value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "KeyguardQuickAffordanceInteractor"
|
private const val TAG = "KeyguardQuickAffordanceInteractor"
|
||||||
private const val DELIMITER = "::"
|
private const val DELIMITER = "::"
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.keyguard.shared.model
|
||||||
|
|
||||||
|
/** Represents a flag that's consumed by the settings or wallpaper picker app. */
|
||||||
|
data class KeyguardPickerFlag(
|
||||||
|
val name: String,
|
||||||
|
val value: Boolean,
|
||||||
|
)
|
||||||
@@ -27,17 +27,22 @@ import com.android.systemui.SysuiTestCase
|
|||||||
import com.android.systemui.flags.FakeFeatureFlags
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
import com.android.systemui.flags.Flags
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
|
import com.android.systemui.settings.UserFileManager
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
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.mock
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -46,6 +51,8 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
@@ -66,12 +73,28 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
underTest = KeyguardQuickAffordanceProvider()
|
underTest = KeyguardQuickAffordanceProvider()
|
||||||
|
val scope = CoroutineScope(IMMEDIATE)
|
||||||
|
val selectionManager =
|
||||||
|
KeyguardQuickAffordanceSelectionManager(
|
||||||
|
context = context,
|
||||||
|
userFileManager =
|
||||||
|
mock<UserFileManager>().apply {
|
||||||
|
whenever(
|
||||||
|
getSharedPreferences(
|
||||||
|
anyString(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(FakeSharedPreferences())
|
||||||
|
},
|
||||||
|
userTracker = userTracker,
|
||||||
|
)
|
||||||
val quickAffordanceRepository =
|
val quickAffordanceRepository =
|
||||||
KeyguardQuickAffordanceRepository(
|
KeyguardQuickAffordanceRepository(
|
||||||
appContext = context,
|
appContext = context,
|
||||||
scope = CoroutineScope(IMMEDIATE),
|
scope = scope,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
selectionManager = selectionManager,
|
||||||
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
|
||||||
configs =
|
configs =
|
||||||
setOf(
|
setOf(
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
FakeKeyguardQuickAffordanceConfig(
|
||||||
@@ -83,6 +106,13 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
|
|||||||
pickerIconResourceId = 2,
|
pickerIconResourceId = 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
legacySettingSyncer =
|
||||||
|
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||||
|
scope = scope,
|
||||||
|
backgroundDispatcher = IMMEDIATE,
|
||||||
|
secureSettings = FakeSettings(),
|
||||||
|
selectionsManager = selectionManager,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
underTest.interactor =
|
underTest.interactor =
|
||||||
KeyguardQuickAffordanceInteractor(
|
KeyguardQuickAffordanceInteractor(
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.keyguard.data.quickaffordance
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.settings.FakeUserTracker
|
||||||
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
|
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
|
||||||
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
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
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var sharedPrefs: FakeSharedPreferences
|
||||||
|
|
||||||
|
private lateinit var underTest: KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
|
|
||||||
|
private lateinit var testScope: TestScope
|
||||||
|
private lateinit var testDispatcher: TestDispatcher
|
||||||
|
private lateinit var selectionManager: KeyguardQuickAffordanceSelectionManager
|
||||||
|
private lateinit var settings: FakeSettings
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
val context: Context = mock()
|
||||||
|
sharedPrefs = FakeSharedPreferences()
|
||||||
|
whenever(context.getSharedPreferences(anyString(), any())).thenReturn(sharedPrefs)
|
||||||
|
val resources: Resources = mock()
|
||||||
|
whenever(resources.getStringArray(R.array.config_keyguardQuickAffordanceDefaults))
|
||||||
|
.thenReturn(emptyArray())
|
||||||
|
whenever(context.resources).thenReturn(resources)
|
||||||
|
|
||||||
|
testDispatcher = UnconfinedTestDispatcher()
|
||||||
|
testScope = TestScope(testDispatcher)
|
||||||
|
selectionManager =
|
||||||
|
KeyguardQuickAffordanceSelectionManager(
|
||||||
|
context = context,
|
||||||
|
userFileManager =
|
||||||
|
mock {
|
||||||
|
whenever(
|
||||||
|
getSharedPreferences(
|
||||||
|
anyString(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(FakeSharedPreferences())
|
||||||
|
},
|
||||||
|
userTracker = FakeUserTracker(),
|
||||||
|
)
|
||||||
|
settings = FakeSettings()
|
||||||
|
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
|
||||||
|
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET, 0)
|
||||||
|
settings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0)
|
||||||
|
|
||||||
|
underTest =
|
||||||
|
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||||
|
scope = testScope,
|
||||||
|
backgroundDispatcher = testDispatcher,
|
||||||
|
secureSettings = settings,
|
||||||
|
selectionsManager = selectionManager,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Setting a setting selects the affordance`() =
|
||||||
|
testScope.runTest {
|
||||||
|
val job = underTest.startSyncing()
|
||||||
|
|
||||||
|
settings.putInt(
|
||||||
|
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
selectionManager
|
||||||
|
.getSelections()
|
||||||
|
.getOrDefault(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||||
|
emptyList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.contains(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Clearing a setting selects the affordance`() =
|
||||||
|
testScope.runTest {
|
||||||
|
val job = underTest.startSyncing()
|
||||||
|
|
||||||
|
settings.putInt(
|
||||||
|
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
settings.putInt(
|
||||||
|
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
selectionManager
|
||||||
|
.getSelections()
|
||||||
|
.getOrDefault(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||||
|
emptyList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.doesNotContain(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Selecting an affordance sets its setting`() =
|
||||||
|
testScope.runTest {
|
||||||
|
val job = underTest.startSyncing()
|
||||||
|
|
||||||
|
selectionManager.setSelections(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
listOf(BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET)
|
||||||
|
)
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(settings.getInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET)).isEqualTo(1)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Unselecting an affordance clears its setting`() =
|
||||||
|
testScope.runTest {
|
||||||
|
val job = underTest.startSyncing()
|
||||||
|
|
||||||
|
selectionManager.setSelections(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
listOf(BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET)
|
||||||
|
)
|
||||||
|
selectionManager.setSelections(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(settings.getInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET)).isEqualTo(0)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,34 +17,66 @@
|
|||||||
|
|
||||||
package com.android.systemui.keyguard.data.quickaffordance
|
package com.android.systemui.keyguard.data.quickaffordance
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.content.pm.UserInfo
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
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 com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var userFileManager: UserFileManager
|
||||||
|
|
||||||
private lateinit var underTest: KeyguardQuickAffordanceSelectionManager
|
private lateinit var underTest: KeyguardQuickAffordanceSelectionManager
|
||||||
|
|
||||||
|
private lateinit var userTracker: FakeUserTracker
|
||||||
|
private lateinit var sharedPrefs: MutableMap<Int, SharedPreferences>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
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(
|
||||||
|
context = context,
|
||||||
|
userFileManager = userFileManager,
|
||||||
|
userTracker = userTracker,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun setSelections() =
|
fun setSelections() = runTest {
|
||||||
runBlocking(IMMEDIATE) {
|
overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
|
||||||
var affordanceIdsBySlotId: Map<String, List<String>>? = null
|
val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
|
||||||
val job = underTest.selections.onEach { affordanceIdsBySlotId = it }.launchIn(this)
|
val job =
|
||||||
|
launch(UnconfinedTestDispatcher()) {
|
||||||
|
underTest.selections.toList(affordanceIdsBySlotId)
|
||||||
|
}
|
||||||
val slotId1 = "slot1"
|
val slotId1 = "slot1"
|
||||||
val slotId2 = "slot2"
|
val slotId2 = "slot2"
|
||||||
val affordanceId1 = "affordance1"
|
val affordanceId1 = "affordance1"
|
||||||
@@ -56,7 +88,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
|||||||
affordanceIds = listOf(affordanceId1),
|
affordanceIds = listOf(affordanceId1),
|
||||||
)
|
)
|
||||||
assertSelections(
|
assertSelections(
|
||||||
affordanceIdsBySlotId,
|
affordanceIdsBySlotId.last(),
|
||||||
mapOf(
|
mapOf(
|
||||||
slotId1 to listOf(affordanceId1),
|
slotId1 to listOf(affordanceId1),
|
||||||
),
|
),
|
||||||
@@ -67,7 +99,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
|||||||
affordanceIds = listOf(affordanceId2),
|
affordanceIds = listOf(affordanceId2),
|
||||||
)
|
)
|
||||||
assertSelections(
|
assertSelections(
|
||||||
affordanceIdsBySlotId,
|
affordanceIdsBySlotId.last(),
|
||||||
mapOf(
|
mapOf(
|
||||||
slotId1 to listOf(affordanceId1),
|
slotId1 to listOf(affordanceId1),
|
||||||
slotId2 to listOf(affordanceId2),
|
slotId2 to listOf(affordanceId2),
|
||||||
@@ -79,7 +111,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
|||||||
affordanceIds = listOf(affordanceId1, affordanceId3),
|
affordanceIds = listOf(affordanceId1, affordanceId3),
|
||||||
)
|
)
|
||||||
assertSelections(
|
assertSelections(
|
||||||
affordanceIdsBySlotId,
|
affordanceIdsBySlotId.last(),
|
||||||
mapOf(
|
mapOf(
|
||||||
slotId1 to listOf(affordanceId1, affordanceId3),
|
slotId1 to listOf(affordanceId1, affordanceId3),
|
||||||
slotId2 to listOf(affordanceId2),
|
slotId2 to listOf(affordanceId2),
|
||||||
@@ -91,7 +123,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
|||||||
affordanceIds = listOf(affordanceId3),
|
affordanceIds = listOf(affordanceId3),
|
||||||
)
|
)
|
||||||
assertSelections(
|
assertSelections(
|
||||||
affordanceIdsBySlotId,
|
affordanceIdsBySlotId.last(),
|
||||||
mapOf(
|
mapOf(
|
||||||
slotId1 to listOf(affordanceId3),
|
slotId1 to listOf(affordanceId3),
|
||||||
slotId2 to listOf(affordanceId2),
|
slotId2 to listOf(affordanceId2),
|
||||||
@@ -103,7 +135,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
|||||||
affordanceIds = listOf(),
|
affordanceIds = listOf(),
|
||||||
)
|
)
|
||||||
assertSelections(
|
assertSelections(
|
||||||
affordanceIdsBySlotId,
|
affordanceIdsBySlotId.last(),
|
||||||
mapOf(
|
mapOf(
|
||||||
slotId1 to listOf(affordanceId3),
|
slotId1 to listOf(affordanceId3),
|
||||||
slotId2 to listOf(),
|
slotId2 to listOf(),
|
||||||
@@ -113,15 +145,184 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun assertSelections(
|
@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(
|
||||||
|
slot1 to listOf(affordance2),
|
||||||
|
slot2 to listOf(affordance3),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
slot1 to listOf(affordance1),
|
||||||
|
slot2 to listOf(affordance2),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selections respects defaults`() = runTest {
|
||||||
|
val slotId1 = "slot1"
|
||||||
|
val slotId2 = "slot2"
|
||||||
|
val affordanceId1 = "affordance1"
|
||||||
|
val affordanceId2 = "affordance2"
|
||||||
|
val affordanceId3 = "affordance3"
|
||||||
|
overrideResource(
|
||||||
|
R.array.config_keyguardQuickAffordanceDefaults,
|
||||||
|
arrayOf(
|
||||||
|
"$slotId1:${listOf(affordanceId1, affordanceId3).joinToString(",")}",
|
||||||
|
"$slotId2:${listOf(affordanceId2).joinToString(",")}",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
|
||||||
|
val job =
|
||||||
|
launch(UnconfinedTestDispatcher()) {
|
||||||
|
underTest.selections.toList(affordanceIdsBySlotId)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSelections(
|
||||||
|
affordanceIdsBySlotId.last(),
|
||||||
|
mapOf(
|
||||||
|
slotId1 to listOf(affordanceId1, affordanceId3),
|
||||||
|
slotId2 to listOf(affordanceId2),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selections ignores defaults after selecting an affordance`() = runTest {
|
||||||
|
val slotId1 = "slot1"
|
||||||
|
val slotId2 = "slot2"
|
||||||
|
val affordanceId1 = "affordance1"
|
||||||
|
val affordanceId2 = "affordance2"
|
||||||
|
val affordanceId3 = "affordance3"
|
||||||
|
overrideResource(
|
||||||
|
R.array.config_keyguardQuickAffordanceDefaults,
|
||||||
|
arrayOf(
|
||||||
|
"$slotId1:${listOf(affordanceId1, affordanceId3).joinToString(",")}",
|
||||||
|
"$slotId2:${listOf(affordanceId2).joinToString(",")}",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
|
||||||
|
val job =
|
||||||
|
launch(UnconfinedTestDispatcher()) {
|
||||||
|
underTest.selections.toList(affordanceIdsBySlotId)
|
||||||
|
}
|
||||||
|
|
||||||
|
underTest.setSelections(slotId1, listOf(affordanceId2))
|
||||||
|
assertSelections(
|
||||||
|
affordanceIdsBySlotId.last(),
|
||||||
|
mapOf(
|
||||||
|
slotId1 to listOf(affordanceId2),
|
||||||
|
slotId2 to listOf(affordanceId2),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selections ignores defaults after clearing a slot`() = runTest {
|
||||||
|
val slotId1 = "slot1"
|
||||||
|
val slotId2 = "slot2"
|
||||||
|
val affordanceId1 = "affordance1"
|
||||||
|
val affordanceId2 = "affordance2"
|
||||||
|
val affordanceId3 = "affordance3"
|
||||||
|
overrideResource(
|
||||||
|
R.array.config_keyguardQuickAffordanceDefaults,
|
||||||
|
arrayOf(
|
||||||
|
"$slotId1:${listOf(affordanceId1, affordanceId3).joinToString(",")}",
|
||||||
|
"$slotId2:${listOf(affordanceId2).joinToString(",")}",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
|
||||||
|
val job =
|
||||||
|
launch(UnconfinedTestDispatcher()) {
|
||||||
|
underTest.selections.toList(affordanceIdsBySlotId)
|
||||||
|
}
|
||||||
|
|
||||||
|
underTest.setSelections(slotId1, listOf())
|
||||||
|
assertSelections(
|
||||||
|
affordanceIdsBySlotId.last(),
|
||||||
|
mapOf(
|
||||||
|
slotId1 to listOf(),
|
||||||
|
slotId2 to listOf(affordanceId2),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertSelections(
|
||||||
observed: Map<String, List<String>>?,
|
observed: Map<String, List<String>>?,
|
||||||
expected: Map<String, List<String>>,
|
expected: Map<String, List<String>>,
|
||||||
) {
|
) {
|
||||||
assertThat(underTest.getSelections()).isEqualTo(expected)
|
assertThat(underTest.getSelections()).isEqualTo(expected)
|
||||||
assertThat(observed).isEqualTo(expected)
|
assertThat(observed).isEqualTo(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,16 @@ import com.android.systemui.R
|
|||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
|
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.android.systemui.util.settings.FakeSettings
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -36,6 +43,8 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -51,12 +60,36 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
config1 = FakeKeyguardQuickAffordanceConfig("built_in:1")
|
config1 = FakeKeyguardQuickAffordanceConfig("built_in:1")
|
||||||
config2 = FakeKeyguardQuickAffordanceConfig("built_in:2")
|
config2 = FakeKeyguardQuickAffordanceConfig("built_in:2")
|
||||||
|
val scope = CoroutineScope(IMMEDIATE)
|
||||||
|
val selectionManager =
|
||||||
|
KeyguardQuickAffordanceSelectionManager(
|
||||||
|
context = context,
|
||||||
|
userFileManager =
|
||||||
|
mock<UserFileManager>().apply {
|
||||||
|
whenever(
|
||||||
|
getSharedPreferences(
|
||||||
|
anyString(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(FakeSharedPreferences())
|
||||||
|
},
|
||||||
|
userTracker = FakeUserTracker(),
|
||||||
|
)
|
||||||
|
|
||||||
underTest =
|
underTest =
|
||||||
KeyguardQuickAffordanceRepository(
|
KeyguardQuickAffordanceRepository(
|
||||||
appContext = context,
|
appContext = context,
|
||||||
scope = CoroutineScope(IMMEDIATE),
|
scope = scope,
|
||||||
|
selectionManager = selectionManager,
|
||||||
|
legacySettingSyncer =
|
||||||
|
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||||
|
scope = scope,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = IMMEDIATE,
|
||||||
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
secureSettings = FakeSettings(),
|
||||||
|
selectionsManager = selectionManager,
|
||||||
|
),
|
||||||
configs = setOf(config1, config2),
|
configs = setOf(config1, config2),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,17 +30,22 @@ import com.android.systemui.flags.Flags
|
|||||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
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.settings.UserTracker
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
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.any
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
@@ -50,6 +55,8 @@ import org.junit.runner.RunWith
|
|||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
import org.junit.runners.Parameterized.Parameter
|
import org.junit.runners.Parameterized.Parameter
|
||||||
import org.junit.runners.Parameterized.Parameters
|
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.eq
|
||||||
import org.mockito.ArgumentMatchers.same
|
import org.mockito.ArgumentMatchers.same
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
@@ -201,7 +208,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Mock private lateinit var lockPatternUtils: LockPatternUtils
|
@Mock private lateinit var lockPatternUtils: LockPatternUtils
|
||||||
@Mock private lateinit var keyguardStateController: KeyguardStateController
|
@Mock private lateinit var keyguardStateController: KeyguardStateController
|
||||||
@Mock private lateinit var userTracker: UserTracker
|
|
||||||
@Mock private lateinit var activityStarter: ActivityStarter
|
@Mock private lateinit var activityStarter: ActivityStarter
|
||||||
@Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
|
@Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
|
||||||
@Mock private lateinit var expandable: Expandable
|
@Mock private lateinit var expandable: Expandable
|
||||||
@@ -214,12 +220,14 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
@JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false
|
@JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false
|
||||||
@JvmField @Parameter(4) var startActivity: Boolean = false
|
@JvmField @Parameter(4) var startActivity: Boolean = false
|
||||||
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
|
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
|
||||||
|
private lateinit var userTracker: UserTracker
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
whenever(expandable.activityLaunchController()).thenReturn(animationController)
|
whenever(expandable.activityLaunchController()).thenReturn(animationController)
|
||||||
|
|
||||||
|
userTracker = FakeUserTracker()
|
||||||
homeControls =
|
homeControls =
|
||||||
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
||||||
val quickAccessWallet =
|
val quickAccessWallet =
|
||||||
@@ -228,12 +236,35 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
val qrCodeScanner =
|
val qrCodeScanner =
|
||||||
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||||
|
val scope = CoroutineScope(IMMEDIATE)
|
||||||
|
val selectionManager =
|
||||||
|
KeyguardQuickAffordanceSelectionManager(
|
||||||
|
context = context,
|
||||||
|
userFileManager =
|
||||||
|
mock<UserFileManager>().apply {
|
||||||
|
whenever(
|
||||||
|
getSharedPreferences(
|
||||||
|
anyString(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(FakeSharedPreferences())
|
||||||
|
},
|
||||||
|
userTracker = userTracker,
|
||||||
|
)
|
||||||
val quickAffordanceRepository =
|
val quickAffordanceRepository =
|
||||||
KeyguardQuickAffordanceRepository(
|
KeyguardQuickAffordanceRepository(
|
||||||
appContext = context,
|
appContext = context,
|
||||||
scope = CoroutineScope(IMMEDIATE),
|
scope = scope,
|
||||||
|
selectionManager = selectionManager,
|
||||||
|
legacySettingSyncer =
|
||||||
|
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||||
|
scope = scope,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = IMMEDIATE,
|
||||||
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
secureSettings = FakeSettings(),
|
||||||
|
selectionsManager = selectionManager,
|
||||||
|
),
|
||||||
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||||
)
|
)
|
||||||
underTest =
|
underTest =
|
||||||
@@ -319,7 +350,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
needStrongAuthAfterBoot: Boolean = true,
|
needStrongAuthAfterBoot: Boolean = true,
|
||||||
keyguardIsUnlocked: Boolean = false,
|
keyguardIsUnlocked: Boolean = false,
|
||||||
) {
|
) {
|
||||||
whenever(userTracker.userHandle).thenReturn(mock())
|
|
||||||
whenever(lockPatternUtils.getStrongAuthForUser(any()))
|
whenever(lockPatternUtils.getStrongAuthForUser(any()))
|
||||||
.thenReturn(
|
.thenReturn(
|
||||||
if (needStrongAuthAfterBoot) {
|
if (needStrongAuthAfterBoot) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.android.systemui.flags.Flags
|
|||||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
@@ -35,11 +36,14 @@ import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAff
|
|||||||
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
|
import com.android.systemui.settings.UserFileManager
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
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.mock
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -53,6 +57,8 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
@@ -89,13 +95,36 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
qrCodeScanner =
|
qrCodeScanner =
|
||||||
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||||
|
val scope = CoroutineScope(IMMEDIATE)
|
||||||
|
|
||||||
|
val selectionManager =
|
||||||
|
KeyguardQuickAffordanceSelectionManager(
|
||||||
|
context = context,
|
||||||
|
userFileManager =
|
||||||
|
mock<UserFileManager>().apply {
|
||||||
|
whenever(
|
||||||
|
getSharedPreferences(
|
||||||
|
anyString(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(FakeSharedPreferences())
|
||||||
|
},
|
||||||
|
userTracker = userTracker,
|
||||||
|
)
|
||||||
val quickAffordanceRepository =
|
val quickAffordanceRepository =
|
||||||
KeyguardQuickAffordanceRepository(
|
KeyguardQuickAffordanceRepository(
|
||||||
appContext = context,
|
appContext = context,
|
||||||
scope = CoroutineScope(IMMEDIATE),
|
scope = scope,
|
||||||
|
selectionManager = selectionManager,
|
||||||
|
legacySettingSyncer =
|
||||||
|
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||||
|
scope = scope,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = IMMEDIATE,
|
||||||
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
secureSettings = FakeSettings(),
|
||||||
|
selectionsManager = selectionManager,
|
||||||
|
),
|
||||||
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||||
)
|
)
|
||||||
featureFlags =
|
featureFlags =
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.android.systemui.flags.Flags
|
|||||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
@@ -38,10 +39,13 @@ import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAff
|
|||||||
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
|
import com.android.systemui.settings.UserFileManager
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
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.any
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -56,6 +60,7 @@ import org.junit.Test
|
|||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
import org.mockito.ArgumentMatchers.anyInt
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
import org.mockito.Mockito.verifyZeroInteractions
|
import org.mockito.Mockito.verifyZeroInteractions
|
||||||
@@ -115,12 +120,35 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
|||||||
whenever(userTracker.userHandle).thenReturn(mock())
|
whenever(userTracker.userHandle).thenReturn(mock())
|
||||||
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
|
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
|
||||||
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
|
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
|
||||||
|
val scope = CoroutineScope(IMMEDIATE)
|
||||||
|
val selectionManager =
|
||||||
|
KeyguardQuickAffordanceSelectionManager(
|
||||||
|
context = context,
|
||||||
|
userFileManager =
|
||||||
|
mock<UserFileManager>().apply {
|
||||||
|
whenever(
|
||||||
|
getSharedPreferences(
|
||||||
|
anyString(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(FakeSharedPreferences())
|
||||||
|
},
|
||||||
|
userTracker = userTracker,
|
||||||
|
)
|
||||||
val quickAffordanceRepository =
|
val quickAffordanceRepository =
|
||||||
KeyguardQuickAffordanceRepository(
|
KeyguardQuickAffordanceRepository(
|
||||||
appContext = context,
|
appContext = context,
|
||||||
scope = CoroutineScope(IMMEDIATE),
|
scope = scope,
|
||||||
|
selectionManager = selectionManager,
|
||||||
|
legacySettingSyncer =
|
||||||
|
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||||
|
scope = scope,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = IMMEDIATE,
|
||||||
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
secureSettings = FakeSettings(),
|
||||||
|
selectionsManager = selectionManager,
|
||||||
|
),
|
||||||
configs =
|
configs =
|
||||||
setOf(
|
setOf(
|
||||||
homeControlsQuickAffordanceConfig,
|
homeControlsQuickAffordanceConfig,
|
||||||
|
|||||||
Reference in New Issue
Block a user