Merge "Multi-user customizable quick affordances." into tm-qpr-dev
This commit is contained in:
@@ -17,27 +17,35 @@
|
||||
|
||||
package com.android.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.ElementsIntoSet
|
||||
|
||||
@Module
|
||||
object KeyguardDataQuickAffordanceModule {
|
||||
@Provides
|
||||
@ElementsIntoSet
|
||||
fun quickAffordanceConfigs(
|
||||
flashlight: FlashlightQuickAffordanceConfig,
|
||||
home: HomeControlsKeyguardQuickAffordanceConfig,
|
||||
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
|
||||
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
|
||||
camera: CameraQuickAffordanceConfig,
|
||||
): Set<KeyguardQuickAffordanceConfig> {
|
||||
return setOf(
|
||||
camera,
|
||||
flashlight,
|
||||
home,
|
||||
quickAccessWallet,
|
||||
qrCodeScanner,
|
||||
)
|
||||
interface KeyguardDataQuickAffordanceModule {
|
||||
@Binds
|
||||
fun providerClientFactory(
|
||||
impl: KeyguardQuickAffordanceProviderClientFactoryImpl,
|
||||
): KeyguardQuickAffordanceProviderClientFactory
|
||||
|
||||
companion object {
|
||||
@Provides
|
||||
@ElementsIntoSet
|
||||
fun quickAffordanceConfigs(
|
||||
flashlight: FlashlightQuickAffordanceConfig,
|
||||
home: HomeControlsKeyguardQuickAffordanceConfig,
|
||||
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
|
||||
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
|
||||
camera: CameraQuickAffordanceConfig,
|
||||
): Set<KeyguardQuickAffordanceConfig> {
|
||||
return setOf(
|
||||
camera,
|
||||
flashlight,
|
||||
home,
|
||||
quickAccessWallet,
|
||||
qrCodeScanner,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||
private val secureSettings: SecureSettings,
|
||||
private val selectionsManager: KeyguardQuickAffordanceSelectionManager,
|
||||
private val selectionsManager: KeyguardQuickAffordanceLocalUserSelectionManager,
|
||||
) {
|
||||
companion object {
|
||||
private val BINDINGS =
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.IntentFilter
|
||||
import android.content.SharedPreferences
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.backup.BackupHelper
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
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.qualifiers.Application
|
||||
import com.android.systemui.settings.UserFileManager
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
|
||||
/**
|
||||
* Manages and provides access to the current "selections" of keyguard quick affordances, answering
|
||||
* the question "which affordances should the keyguard show?" for the user associated with the
|
||||
* System UI process.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SysUISingleton
|
||||
class KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
@Inject
|
||||
constructor(
|
||||
@Application context: Context,
|
||||
private val userFileManager: UserFileManager,
|
||||
private val userTracker: UserTracker,
|
||||
broadcastDispatcher: BroadcastDispatcher,
|
||||
) : KeyguardQuickAffordanceSelectionManager {
|
||||
|
||||
private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
|
||||
* initial value.
|
||||
*/
|
||||
private val backupRestorationEvents: Flow<Unit> =
|
||||
broadcastDispatcher.broadcastFlow(
|
||||
filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
|
||||
flags = Context.RECEIVER_NOT_EXPORTED,
|
||||
permission = BackupHelper.PERMISSION_SELF,
|
||||
)
|
||||
|
||||
override val selections: Flow<Map<String, List<String>>> =
|
||||
combine(
|
||||
userId,
|
||||
backupRestorationEvents.onStart {
|
||||
// We emit an initial event to make sure that the combine emits at least once,
|
||||
// even if we never get a Backup & Restore restoration event (which is the most
|
||||
// common case anyway as restoration really only happens on initial device
|
||||
// setup).
|
||||
emit(Unit)
|
||||
}
|
||||
) { _, _ -> }
|
||||
.flatMapLatest {
|
||||
conflatedCallbackFlow {
|
||||
// We want to instantiate a new SharedPreferences instance each time either the
|
||||
// user ID changes or we have a backup & restore restoration event. The reason
|
||||
// is that our sharedPrefs instance needs to be replaced with a new one as it
|
||||
// depends on the user ID and when the B&R job completes, the backing file is
|
||||
// replaced but the existing instance still has a stale in-memory cache.
|
||||
sharedPrefs = instantiateSharedPrefs()
|
||||
|
||||
val listener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||
trySend(getSelections())
|
||||
}
|
||||
|
||||
sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
|
||||
send(getSelections())
|
||||
|
||||
awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelections(): Map<String, List<String>> {
|
||||
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
|
||||
}
|
||||
|
||||
override fun setSelections(
|
||||
slotId: String,
|
||||
affordanceIds: List<String>,
|
||||
) {
|
||||
val key = "$KEY_PREFIX_SLOT$slotId"
|
||||
val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
|
||||
sharedPrefs.edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
private fun instantiateSharedPrefs(): SharedPreferences {
|
||||
return userFileManager.getSharedPreferences(
|
||||
FILE_NAME,
|
||||
Context.MODE_PRIVATE,
|
||||
userTracker.userId,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KeyguardQuickAffordancePrimaryUserSelectionManager"
|
||||
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 = ","
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
||||
interface KeyguardQuickAffordanceProviderClientFactory {
|
||||
fun create(): KeyguardQuickAffordanceProviderClient
|
||||
}
|
||||
|
||||
class KeyguardQuickAffordanceProviderClientFactoryImpl
|
||||
@Inject
|
||||
constructor(
|
||||
private val userTracker: UserTracker,
|
||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||
) : KeyguardQuickAffordanceProviderClientFactory {
|
||||
override fun create(): KeyguardQuickAffordanceProviderClient {
|
||||
return KeyguardQuickAffordanceProviderClientImpl(
|
||||
context = userTracker.userContext,
|
||||
backgroundDispatcher = backgroundDispatcher,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.os.UserHandle
|
||||
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.qualifiers.Application
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Manages and provides access to the current "selections" of keyguard quick affordances, answering
|
||||
* the question "which affordances should the keyguard show?" for users associated with other System
|
||||
* UI processes.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SysUISingleton
|
||||
class KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val userTracker: UserTracker,
|
||||
private val clientFactory: KeyguardQuickAffordanceProviderClientFactory,
|
||||
private val userHandle: UserHandle,
|
||||
) : KeyguardQuickAffordanceSelectionManager {
|
||||
|
||||
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 clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
|
||||
userId
|
||||
.distinctUntilChanged()
|
||||
.map { selectedUserId ->
|
||||
if (userHandle.isSystem && userHandle.identifier != selectedUserId) {
|
||||
clientFactory.create()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = null,
|
||||
)
|
||||
|
||||
private val _selections: StateFlow<Map<String, List<String>>> =
|
||||
clientOrNull
|
||||
.flatMapLatest { client ->
|
||||
client?.observeSelections()?.map { selections ->
|
||||
buildMap<String, List<String>> {
|
||||
selections.forEach { selection ->
|
||||
val slotId = selection.slotId
|
||||
val affordanceIds = (get(slotId) ?: emptyList()).toMutableList()
|
||||
affordanceIds.add(selection.affordanceId)
|
||||
put(slotId, affordanceIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
?: emptyFlow()
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyMap(),
|
||||
)
|
||||
|
||||
override val selections: Flow<Map<String, List<String>>> = _selections
|
||||
|
||||
override fun getSelections(): Map<String, List<String>> {
|
||||
return _selections.value
|
||||
}
|
||||
|
||||
override fun setSelections(slotId: String, affordanceIds: List<String>) {
|
||||
clientOrNull.value?.let { client ->
|
||||
scope.launch {
|
||||
client.deleteAllSelections(slotId = slotId)
|
||||
affordanceIds.forEach { affordanceId ->
|
||||
client.insertSelection(slotId = slotId, affordanceId = affordanceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KeyguardQuickAffordanceMultiUserSelectionManager"
|
||||
}
|
||||
}
|
||||
@@ -17,153 +17,22 @@
|
||||
|
||||
package com.android.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.content.SharedPreferences
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.backup.BackupHelper
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
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.qualifiers.Application
|
||||
import com.android.systemui.settings.UserFileManager
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
|
||||
/**
|
||||
* Manages and provides access to the current "selections" of keyguard quick affordances, answering
|
||||
* the question "which affordances should the keyguard show?".
|
||||
* Defines interface for classes that manage and provide access to the current "selections" of
|
||||
* keyguard quick affordances, answering the question "which affordances should the keyguard show?".
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SysUISingleton
|
||||
class KeyguardQuickAffordanceSelectionManager
|
||||
@Inject
|
||||
constructor(
|
||||
@Application context: Context,
|
||||
private val userFileManager: UserFileManager,
|
||||
private val userTracker: UserTracker,
|
||||
broadcastDispatcher: BroadcastDispatcher,
|
||||
) {
|
||||
|
||||
private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
|
||||
* initial value.
|
||||
*/
|
||||
private val backupRestorationEvents: Flow<Unit> =
|
||||
broadcastDispatcher.broadcastFlow(
|
||||
filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
|
||||
flags = Context.RECEIVER_NOT_EXPORTED,
|
||||
permission = BackupHelper.PERMISSION_SELF,
|
||||
)
|
||||
interface KeyguardQuickAffordanceSelectionManager {
|
||||
|
||||
/** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */
|
||||
val selections: Flow<Map<String, List<String>>> =
|
||||
combine(
|
||||
userId,
|
||||
backupRestorationEvents.onStart {
|
||||
// We emit an initial event to make sure that the combine emits at least once,
|
||||
// even
|
||||
// if we never get a Backup & Restore restoration event (which is the most
|
||||
// common
|
||||
// case anyway as restoration really only happens on initial device setup).
|
||||
emit(Unit)
|
||||
}
|
||||
) { _, _ ->
|
||||
}
|
||||
.flatMapLatest {
|
||||
conflatedCallbackFlow {
|
||||
// We want to instantiate a new SharedPreferences instance each time either the
|
||||
// user
|
||||
// ID changes or we have a backup & restore restoration event. The reason is
|
||||
// that
|
||||
// our sharedPrefs instance needs to be replaced with a new one as it depends on
|
||||
// the
|
||||
// user ID and when the B&R job completes, the backing file is replaced but the
|
||||
// existing instance still has a stale in-memory cache.
|
||||
sharedPrefs = instantiateSharedPrefs()
|
||||
|
||||
val listener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||
trySend(getSelections())
|
||||
}
|
||||
|
||||
sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
|
||||
send(getSelections())
|
||||
|
||||
awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
|
||||
}
|
||||
}
|
||||
val selections: Flow<Map<String, List<String>>>
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in
|
||||
* descending priority order.
|
||||
*/
|
||||
fun getSelections(): Map<String, List<String>> {
|
||||
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
|
||||
}
|
||||
fun getSelections(): Map<String, List<String>>
|
||||
|
||||
/**
|
||||
* Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
|
||||
@@ -172,25 +41,9 @@ constructor(
|
||||
fun setSelections(
|
||||
slotId: String,
|
||||
affordanceIds: List<String>,
|
||||
) {
|
||||
val key = "$KEY_PREFIX_SLOT$slotId"
|
||||
val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
|
||||
sharedPrefs.edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
private fun instantiateSharedPrefs(): SharedPreferences {
|
||||
return userFileManager.getSharedPreferences(
|
||||
FILE_NAME,
|
||||
Context.MODE_PRIVATE,
|
||||
userTracker.userId,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KeyguardQuickAffordanceSelectionManager"
|
||||
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 = ","
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,45 +18,93 @@
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import android.os.UserHandle
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
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.UserTracker
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/** Abstracts access to application state related to keyguard quick affordances. */
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SysUISingleton
|
||||
class KeyguardQuickAffordanceRepository
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val appContext: Context,
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val selectionManager: KeyguardQuickAffordanceSelectionManager,
|
||||
private val localUserSelectionManager: KeyguardQuickAffordanceLocalUserSelectionManager,
|
||||
private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager,
|
||||
private val userTracker: UserTracker,
|
||||
legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer,
|
||||
private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
|
||||
dumpManager: DumpManager,
|
||||
userHandle: UserHandle,
|
||||
) {
|
||||
private val userId: Flow<Int> =
|
||||
ConflatedCallbackFlow.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 selectionManager: StateFlow<KeyguardQuickAffordanceSelectionManager> =
|
||||
userId
|
||||
.distinctUntilChanged()
|
||||
.map { selectedUserId ->
|
||||
if (userHandle.identifier == selectedUserId) {
|
||||
localUserSelectionManager
|
||||
} else {
|
||||
remoteUserSelectionManager
|
||||
}
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = localUserSelectionManager,
|
||||
)
|
||||
|
||||
/**
|
||||
* List of [KeyguardQuickAffordanceConfig] instances of the affordances at the slot with the
|
||||
* given ID. The configs are sorted in descending priority order.
|
||||
*/
|
||||
val selections: StateFlow<Map<String, List<KeyguardQuickAffordanceConfig>>> =
|
||||
selectionManager.selections
|
||||
.map { selectionsBySlotId ->
|
||||
selectionsBySlotId.mapValues { (_, selections) ->
|
||||
configs.filter { selections.contains(it.key) }
|
||||
selectionManager
|
||||
.flatMapLatest { selectionManager ->
|
||||
selectionManager.selections.map { selectionsBySlotId ->
|
||||
selectionsBySlotId.mapValues { (_, selections) ->
|
||||
configs.filter { selections.contains(it.key) }
|
||||
}
|
||||
}
|
||||
}
|
||||
.stateIn(
|
||||
@@ -99,7 +147,7 @@ constructor(
|
||||
* slot with the given ID. The configs are sorted in descending priority order.
|
||||
*/
|
||||
fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
|
||||
val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList())
|
||||
val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
|
||||
return configs.filter { selections.contains(it.key) }
|
||||
}
|
||||
|
||||
@@ -108,7 +156,7 @@ constructor(
|
||||
* are sorted in descending priority order.
|
||||
*/
|
||||
fun getSelections(): Map<String, List<String>> {
|
||||
return selectionManager.getSelections()
|
||||
return selectionManager.value.getSelections()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +167,7 @@ constructor(
|
||||
slotId: String,
|
||||
affordanceIds: List<String>,
|
||||
) {
|
||||
selectionManager.setSelections(
|
||||
selectionManager.value.setSelections(
|
||||
slotId = slotId,
|
||||
affordanceIds = affordanceIds,
|
||||
)
|
||||
@@ -188,6 +236,7 @@ constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KeyguardQuickAffordanceRepository"
|
||||
private const val SLOT_CONFIG_DELIMITER = ":"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,8 +190,6 @@ constructor(
|
||||
|
||||
/** Returns affordance IDs indexed by slot ID, for all known slots. */
|
||||
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
|
||||
check(isUsingRepository)
|
||||
|
||||
val slots = repository.get().getSlotPickerRepresentations()
|
||||
val selections = repository.get().getSelections()
|
||||
val affordanceById =
|
||||
@@ -312,8 +310,6 @@ constructor(
|
||||
|
||||
suspend fun getAffordancePickerRepresentations():
|
||||
List<KeyguardQuickAffordancePickerRepresentation> {
|
||||
check(isUsingRepository)
|
||||
|
||||
return repository.get().getAffordancePickerRepresentations()
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.user;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.settingslib.users.EditUserInfoController;
|
||||
import com.android.systemui.user.data.repository.UserRepositoryModule;
|
||||
@@ -51,4 +52,22 @@ public abstract class UserModule {
|
||||
@IntoMap
|
||||
@ClassKey(UserSwitcherActivity.class)
|
||||
public abstract Activity provideUserSwitcherActivity(UserSwitcherActivity activity);
|
||||
|
||||
/**
|
||||
* Provides the {@link UserHandle} for the user associated with this System UI process.
|
||||
*
|
||||
* <p>Note that this is static and unchanging for the life-time of the process we are running
|
||||
* in. It can be <i>different</i> from the user that is the currently-selected user, which may
|
||||
* be associated with a different System UI process.
|
||||
*
|
||||
* <p>For example, the System UI process which creates all the windows and renders UI is always
|
||||
* the one associated with the primary user on the device. However, if the user is switched to
|
||||
* another, non-primary user (for example user "X"), then a secondary System UI process will be
|
||||
* spawned. While the original primary user process continues to be the only one rendering UI,
|
||||
* the new system UI process may be used for things like file or content access.
|
||||
*/
|
||||
@Provides
|
||||
public static UserHandle provideUserHandle() {
|
||||
return new UserHandle(UserHandle.myUserId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard
|
||||
import android.content.ContentValues
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ProviderInfo
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
import com.android.systemui.SystemUIAppComponentFactoryBase
|
||||
@@ -27,8 +28,10 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
@@ -74,8 +77,8 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
|
||||
|
||||
underTest = KeyguardQuickAffordanceProvider()
|
||||
val scope = CoroutineScope(IMMEDIATE)
|
||||
val selectionManager =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
val localUserSelectionManager =
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager =
|
||||
mock<UserFileManager>().apply {
|
||||
@@ -91,11 +94,20 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
|
||||
userTracker = userTracker,
|
||||
broadcastDispatcher = fakeBroadcastDispatcher,
|
||||
)
|
||||
val remoteUserSelectionManager =
|
||||
KeyguardQuickAffordanceRemoteUserSelectionManager(
|
||||
scope = scope,
|
||||
userTracker = userTracker,
|
||||
clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
val quickAffordanceRepository =
|
||||
KeyguardQuickAffordanceRepository(
|
||||
appContext = context,
|
||||
scope = scope,
|
||||
selectionManager = selectionManager,
|
||||
localUserSelectionManager = localUserSelectionManager,
|
||||
remoteUserSelectionManager = remoteUserSelectionManager,
|
||||
userTracker = userTracker,
|
||||
configs =
|
||||
setOf(
|
||||
FakeKeyguardQuickAffordanceConfig(
|
||||
@@ -114,9 +126,10 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
|
||||
scope = scope,
|
||||
backgroundDispatcher = IMMEDIATE,
|
||||
secureSettings = FakeSettings(),
|
||||
selectionsManager = selectionManager,
|
||||
selectionsManager = localUserSelectionManager,
|
||||
),
|
||||
dumpManager = mock(),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
underTest.interactor =
|
||||
KeyguardQuickAffordanceInteractor(
|
||||
|
||||
@@ -57,7 +57,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var testScope: TestScope
|
||||
private lateinit var testDispatcher: TestDispatcher
|
||||
private lateinit var selectionManager: KeyguardQuickAffordanceSelectionManager
|
||||
private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
private lateinit var settings: FakeSettings
|
||||
|
||||
@Before
|
||||
@@ -75,7 +75,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
|
||||
testDispatcher = UnconfinedTestDispatcher()
|
||||
testScope = TestScope(testDispatcher)
|
||||
selectionManager =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager =
|
||||
mock {
|
||||
|
||||
@@ -52,11 +52,11 @@ import org.mockito.MockitoAnnotations
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
||||
class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var userFileManager: UserFileManager
|
||||
|
||||
private lateinit var underTest: KeyguardQuickAffordanceSelectionManager
|
||||
private lateinit var underTest: KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
|
||||
private lateinit var userTracker: FakeUserTracker
|
||||
private lateinit var sharedPrefs: MutableMap<Int, SharedPreferences>
|
||||
@@ -74,7 +74,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
|
||||
Dispatchers.setMain(dispatcher)
|
||||
|
||||
underTest =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager = userFileManager,
|
||||
userTracker = userTracker,
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.pm.UserInfo
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.settings.FakeUserTracker
|
||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
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.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var userHandle: UserHandle
|
||||
|
||||
private lateinit var underTest: KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
|
||||
private lateinit var clientFactory: FakeKeyguardQuickAffordanceProviderClientFactory
|
||||
private lateinit var testScope: TestScope
|
||||
private lateinit var testDispatcher: TestDispatcher
|
||||
private lateinit var userTracker: FakeUserTracker
|
||||
private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
|
||||
private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
|
||||
whenever(userHandle.isSystem).thenReturn(true)
|
||||
client1 = FakeKeyguardQuickAffordanceProviderClient()
|
||||
client2 = FakeKeyguardQuickAffordanceProviderClient()
|
||||
|
||||
userTracker = FakeUserTracker()
|
||||
userTracker.set(
|
||||
userInfos =
|
||||
listOf(
|
||||
UserInfo(
|
||||
UserHandle.USER_SYSTEM,
|
||||
"Primary",
|
||||
/* flags= */ 0,
|
||||
),
|
||||
UserInfo(
|
||||
OTHER_USER_ID_1,
|
||||
"Secondary 1",
|
||||
/* flags= */ 0,
|
||||
),
|
||||
UserInfo(
|
||||
OTHER_USER_ID_2,
|
||||
"Secondary 2",
|
||||
/* flags= */ 0,
|
||||
),
|
||||
),
|
||||
selectedUserIndex = 0,
|
||||
)
|
||||
|
||||
clientFactory =
|
||||
FakeKeyguardQuickAffordanceProviderClientFactory(
|
||||
userTracker,
|
||||
) { selectedUserId ->
|
||||
when (selectedUserId) {
|
||||
OTHER_USER_ID_1 -> client1
|
||||
OTHER_USER_ID_2 -> client2
|
||||
else -> error("No client set-up for user $selectedUserId!")
|
||||
}
|
||||
}
|
||||
|
||||
testDispatcher = StandardTestDispatcher()
|
||||
testScope = TestScope(testDispatcher)
|
||||
|
||||
underTest =
|
||||
KeyguardQuickAffordanceRemoteUserSelectionManager(
|
||||
scope = testScope.backgroundScope,
|
||||
userTracker = userTracker,
|
||||
clientFactory = clientFactory,
|
||||
userHandle = userHandle,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `selections - primary user process`() =
|
||||
testScope.runTest {
|
||||
val values = mutableListOf<Map<String, List<String>>>()
|
||||
val job = launch { underTest.selections.toList(values) }
|
||||
|
||||
runCurrent()
|
||||
assertThat(values.last()).isEmpty()
|
||||
|
||||
client1.insertSelection(
|
||||
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
|
||||
)
|
||||
client2.insertSelection(
|
||||
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
|
||||
)
|
||||
|
||||
userTracker.set(
|
||||
userInfos = userTracker.userProfiles,
|
||||
selectedUserIndex = 1,
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(values.last())
|
||||
.isEqualTo(
|
||||
mapOf(
|
||||
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
|
||||
listOf(
|
||||
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
userTracker.set(
|
||||
userInfos = userTracker.userProfiles,
|
||||
selectedUserIndex = 2,
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(values.last())
|
||||
.isEqualTo(
|
||||
mapOf(
|
||||
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
|
||||
listOf(
|
||||
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `selections - secondary user process - always empty`() =
|
||||
testScope.runTest {
|
||||
whenever(userHandle.isSystem).thenReturn(false)
|
||||
val values = mutableListOf<Map<String, List<String>>>()
|
||||
val job = launch { underTest.selections.toList(values) }
|
||||
|
||||
runCurrent()
|
||||
assertThat(values.last()).isEmpty()
|
||||
|
||||
client1.insertSelection(
|
||||
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
|
||||
)
|
||||
userTracker.set(
|
||||
userInfos = userTracker.userProfiles,
|
||||
selectedUserIndex = 1,
|
||||
)
|
||||
runCurrent()
|
||||
assertThat(values.last()).isEmpty()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setSelections() =
|
||||
testScope.runTest {
|
||||
userTracker.set(
|
||||
userInfos = userTracker.userProfiles,
|
||||
selectedUserIndex = 1,
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
underTest.setSelections(
|
||||
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||
affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(underTest.getSelections())
|
||||
.isEqualTo(
|
||||
mapOf(
|
||||
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
|
||||
listOf(
|
||||
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OTHER_USER_ID_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
|
||||
private const val OTHER_USER_ID_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,23 @@
|
||||
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import android.content.pm.UserInfo
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
|
||||
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.KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
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.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
|
||||
import com.android.systemui.util.FakeSharedPreferences
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
@@ -39,6 +45,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.yield
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -55,14 +62,24 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
|
||||
private lateinit var config2: FakeKeyguardQuickAffordanceConfig
|
||||
private lateinit var userTracker: FakeUserTracker
|
||||
private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
|
||||
private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
config1 = FakeKeyguardQuickAffordanceConfig("built_in:1")
|
||||
config2 = FakeKeyguardQuickAffordanceConfig("built_in:2")
|
||||
config1 =
|
||||
FakeKeyguardQuickAffordanceConfig(
|
||||
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
|
||||
)
|
||||
config2 =
|
||||
FakeKeyguardQuickAffordanceConfig(
|
||||
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
|
||||
)
|
||||
val scope = CoroutineScope(IMMEDIATE)
|
||||
val selectionManager =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
userTracker = FakeUserTracker()
|
||||
val localUserSelectionManager =
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager =
|
||||
mock<UserFileManager>().apply {
|
||||
@@ -75,24 +92,45 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
|
||||
)
|
||||
.thenReturn(FakeSharedPreferences())
|
||||
},
|
||||
userTracker = FakeUserTracker(),
|
||||
userTracker = userTracker,
|
||||
broadcastDispatcher = fakeBroadcastDispatcher,
|
||||
)
|
||||
client1 = FakeKeyguardQuickAffordanceProviderClient()
|
||||
client2 = FakeKeyguardQuickAffordanceProviderClient()
|
||||
val remoteUserSelectionManager =
|
||||
KeyguardQuickAffordanceRemoteUserSelectionManager(
|
||||
scope = scope,
|
||||
userTracker = userTracker,
|
||||
clientFactory =
|
||||
FakeKeyguardQuickAffordanceProviderClientFactory(
|
||||
userTracker,
|
||||
) { selectedUserId ->
|
||||
when (selectedUserId) {
|
||||
SECONDARY_USER_1 -> client1
|
||||
SECONDARY_USER_2 -> client2
|
||||
else -> error("No set-up client for user $selectedUserId!")
|
||||
}
|
||||
},
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
|
||||
underTest =
|
||||
KeyguardQuickAffordanceRepository(
|
||||
appContext = context,
|
||||
scope = scope,
|
||||
selectionManager = selectionManager,
|
||||
localUserSelectionManager = localUserSelectionManager,
|
||||
remoteUserSelectionManager = remoteUserSelectionManager,
|
||||
userTracker = userTracker,
|
||||
legacySettingSyncer =
|
||||
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||
scope = scope,
|
||||
backgroundDispatcher = IMMEDIATE,
|
||||
secureSettings = FakeSettings(),
|
||||
selectionsManager = selectionManager,
|
||||
selectionsManager = localUserSelectionManager,
|
||||
),
|
||||
configs = setOf(config1, config2),
|
||||
dumpManager = mock(),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -187,7 +225,53 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun assertSelections(
|
||||
@Test
|
||||
fun `selections for secondary user`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
userTracker.set(
|
||||
userInfos =
|
||||
listOf(
|
||||
UserInfo(
|
||||
UserHandle.USER_SYSTEM,
|
||||
"Primary",
|
||||
/* flags= */ 0,
|
||||
),
|
||||
UserInfo(
|
||||
SECONDARY_USER_1,
|
||||
"Secondary 1",
|
||||
/* flags= */ 0,
|
||||
),
|
||||
UserInfo(
|
||||
SECONDARY_USER_2,
|
||||
"Secondary 2",
|
||||
/* flags= */ 0,
|
||||
),
|
||||
),
|
||||
selectedUserIndex = 2,
|
||||
)
|
||||
client2.insertSelection(
|
||||
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
|
||||
)
|
||||
val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
|
||||
val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
|
||||
yield()
|
||||
|
||||
assertSelections(
|
||||
observed = observed.last(),
|
||||
expected =
|
||||
mapOf(
|
||||
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
|
||||
listOf(
|
||||
config2,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun assertSelections(
|
||||
observed: Map<String, List<KeyguardQuickAffordanceConfig>>?,
|
||||
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
|
||||
) {
|
||||
@@ -201,5 +285,7 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
private const val SECONDARY_USER_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
|
||||
private const val SECONDARY_USER_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -29,9 +30,11 @@ import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
|
||||
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.KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
||||
@@ -237,8 +240,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
||||
val qrCodeScanner =
|
||||
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||
val scope = CoroutineScope(IMMEDIATE)
|
||||
val selectionManager =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
val localUserSelectionManager =
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager =
|
||||
mock<UserFileManager>().apply {
|
||||
@@ -254,20 +257,30 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
||||
userTracker = userTracker,
|
||||
broadcastDispatcher = fakeBroadcastDispatcher,
|
||||
)
|
||||
val remoteUserSelectionManager =
|
||||
KeyguardQuickAffordanceRemoteUserSelectionManager(
|
||||
scope = scope,
|
||||
userTracker = userTracker,
|
||||
clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
val quickAffordanceRepository =
|
||||
KeyguardQuickAffordanceRepository(
|
||||
appContext = context,
|
||||
scope = scope,
|
||||
selectionManager = selectionManager,
|
||||
localUserSelectionManager = localUserSelectionManager,
|
||||
remoteUserSelectionManager = remoteUserSelectionManager,
|
||||
userTracker = userTracker,
|
||||
legacySettingSyncer =
|
||||
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||
scope = scope,
|
||||
backgroundDispatcher = IMMEDIATE,
|
||||
secureSettings = FakeSettings(),
|
||||
selectionsManager = selectionManager,
|
||||
selectionsManager = localUserSelectionManager,
|
||||
),
|
||||
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||
dumpManager = mock(),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
underTest =
|
||||
KeyguardQuickAffordanceInteractor(
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -26,9 +27,11 @@ import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
|
||||
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.KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
||||
@@ -98,8 +101,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
||||
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||
val scope = CoroutineScope(IMMEDIATE)
|
||||
|
||||
val selectionManager =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
val localUserSelectionManager =
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager =
|
||||
mock<UserFileManager>().apply {
|
||||
@@ -115,20 +118,30 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
||||
userTracker = userTracker,
|
||||
broadcastDispatcher = fakeBroadcastDispatcher,
|
||||
)
|
||||
val remoteUserSelectionManager =
|
||||
KeyguardQuickAffordanceRemoteUserSelectionManager(
|
||||
scope = scope,
|
||||
userTracker = userTracker,
|
||||
clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
val quickAffordanceRepository =
|
||||
KeyguardQuickAffordanceRepository(
|
||||
appContext = context,
|
||||
scope = scope,
|
||||
selectionManager = selectionManager,
|
||||
localUserSelectionManager = localUserSelectionManager,
|
||||
remoteUserSelectionManager = remoteUserSelectionManager,
|
||||
userTracker = userTracker,
|
||||
legacySettingSyncer =
|
||||
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||
scope = scope,
|
||||
backgroundDispatcher = IMMEDIATE,
|
||||
secureSettings = FakeSettings(),
|
||||
selectionsManager = selectionManager,
|
||||
selectionsManager = localUserSelectionManager,
|
||||
),
|
||||
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||
dumpManager = mock(),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.keyguard.ui.viewmodel
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -27,9 +28,11 @@ import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
|
||||
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.KeyguardQuickAffordanceLocalUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
|
||||
@@ -121,8 +124,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
||||
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
|
||||
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
|
||||
val scope = CoroutineScope(IMMEDIATE)
|
||||
val selectionManager =
|
||||
KeyguardQuickAffordanceSelectionManager(
|
||||
val localUserSelectionManager =
|
||||
KeyguardQuickAffordanceLocalUserSelectionManager(
|
||||
context = context,
|
||||
userFileManager =
|
||||
mock<UserFileManager>().apply {
|
||||
@@ -138,17 +141,26 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
||||
userTracker = userTracker,
|
||||
broadcastDispatcher = fakeBroadcastDispatcher,
|
||||
)
|
||||
val remoteUserSelectionManager =
|
||||
KeyguardQuickAffordanceRemoteUserSelectionManager(
|
||||
scope = scope,
|
||||
userTracker = userTracker,
|
||||
clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
val quickAffordanceRepository =
|
||||
KeyguardQuickAffordanceRepository(
|
||||
appContext = context,
|
||||
scope = scope,
|
||||
selectionManager = selectionManager,
|
||||
localUserSelectionManager = localUserSelectionManager,
|
||||
remoteUserSelectionManager = remoteUserSelectionManager,
|
||||
userTracker = userTracker,
|
||||
legacySettingSyncer =
|
||||
KeyguardQuickAffordanceLegacySettingSyncer(
|
||||
scope = scope,
|
||||
backgroundDispatcher = IMMEDIATE,
|
||||
secureSettings = FakeSettings(),
|
||||
selectionsManager = selectionManager,
|
||||
selectionsManager = localUserSelectionManager,
|
||||
),
|
||||
configs =
|
||||
setOf(
|
||||
@@ -157,6 +169,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
||||
qrCodeScannerAffordanceConfig,
|
||||
),
|
||||
dumpManager = mock(),
|
||||
userHandle = UserHandle.SYSTEM,
|
||||
)
|
||||
underTest =
|
||||
KeyguardBottomAreaViewModel(
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
|
||||
|
||||
class FakeKeyguardQuickAffordanceProviderClientFactory(
|
||||
private val userTracker: UserTracker,
|
||||
private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
|
||||
FakeKeyguardQuickAffordanceProviderClient()
|
||||
},
|
||||
) : KeyguardQuickAffordanceProviderClientFactory {
|
||||
|
||||
override fun create(): KeyguardQuickAffordanceProviderClient {
|
||||
return callback(userTracker.userId)
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,8 @@ class FakeUserTracker(
|
||||
_userId = _userInfo.id
|
||||
_userHandle = UserHandle.of(_userId)
|
||||
|
||||
callbacks.forEach { it.onUserChanged(_userId, userContext) }
|
||||
val copy = callbacks.toList()
|
||||
copy.forEach { it.onUserChanged(_userId, userContext) }
|
||||
}
|
||||
|
||||
fun onProfileChanged() {
|
||||
|
||||
Reference in New Issue
Block a user