1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package com.android.systemui.keyguard.data.quickaffordance 19 20 import android.content.Context 21 import android.os.UserHandle 22 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 23 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 24 import com.android.systemui.dagger.SysUISingleton 25 import com.android.systemui.dagger.qualifiers.Application 26 import com.android.systemui.settings.UserTracker 27 import com.android.systemui.shared.customization.data.content.CustomizationProviderClient 28 import javax.inject.Inject 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.ExperimentalCoroutinesApi 31 import kotlinx.coroutines.channels.awaitClose 32 import kotlinx.coroutines.flow.Flow 33 import kotlinx.coroutines.flow.SharingStarted 34 import kotlinx.coroutines.flow.StateFlow 35 import kotlinx.coroutines.flow.distinctUntilChanged 36 import kotlinx.coroutines.flow.emptyFlow 37 import kotlinx.coroutines.flow.flatMapLatest 38 import kotlinx.coroutines.flow.map 39 import kotlinx.coroutines.flow.stateIn 40 import kotlinx.coroutines.launch 41 42 /** 43 * Manages and provides access to the current "selections" of keyguard quick affordances, answering 44 * the question "which affordances should the keyguard show?" for users associated with other System 45 * UI processes. 46 */ 47 @OptIn(ExperimentalCoroutinesApi::class) 48 @SysUISingleton 49 class KeyguardQuickAffordanceRemoteUserSelectionManager 50 @Inject 51 constructor( 52 @Application private val scope: CoroutineScope, 53 private val userTracker: UserTracker, 54 private val clientFactory: KeyguardQuickAffordanceProviderClientFactory, 55 private val userHandle: UserHandle, 56 ) : KeyguardQuickAffordanceSelectionManager { 57 58 private val userId: Flow<Int> = conflatedCallbackFlow { 59 val callback = 60 object : UserTracker.Callback { 61 override fun onUserChanged(newUser: Int, userContext: Context) { 62 trySendWithFailureLogging(newUser, TAG) 63 } 64 } 65 66 userTracker.addCallback(callback) { it.run() } 67 trySendWithFailureLogging(userTracker.userId, TAG) 68 69 awaitClose { userTracker.removeCallback(callback) } 70 } 71 72 private val clientOrNull: StateFlow<CustomizationProviderClient?> = 73 userId 74 .distinctUntilChanged() 75 .map { selectedUserId -> 76 if (userHandle.isSystem && userHandle.identifier != selectedUserId) { 77 clientFactory.create() 78 } else { 79 null 80 } 81 } 82 .stateIn( 83 scope = scope, 84 started = SharingStarted.Eagerly, 85 initialValue = null, 86 ) 87 88 private val _selections: StateFlow<Map<String, List<String>>> = 89 clientOrNull 90 .flatMapLatest { client -> 91 client?.observeSelections()?.map { selections -> 92 buildMap<String, List<String>> { 93 selections.forEach { selection -> 94 val slotId = selection.slotId 95 val affordanceIds = (get(slotId) ?: emptyList()).toMutableList() 96 affordanceIds.add(selection.affordanceId) 97 put(slotId, affordanceIds) 98 } 99 } 100 } 101 ?: emptyFlow() 102 } 103 .stateIn( 104 scope = scope, 105 started = SharingStarted.Eagerly, 106 initialValue = emptyMap(), 107 ) 108 109 override val selections: Flow<Map<String, List<String>>> = _selections 110 111 override fun getSelections(): Map<String, List<String>> { 112 return _selections.value 113 } 114 115 override fun setSelections(slotId: String, affordanceIds: List<String>) { 116 clientOrNull.value?.let { client -> 117 scope.launch { 118 client.deleteAllSelections(slotId = slotId) 119 affordanceIds.forEach { affordanceId -> 120 client.insertSelection(slotId = slotId, affordanceId = affordanceId) 121 } 122 } 123 } 124 } 125 126 companion object { 127 private const val TAG = "KeyguardQuickAffordanceMultiUserSelectionManager" 128 } 129 } 130