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.content.Intent 22 import android.graphics.drawable.Drawable 23 import android.service.quickaccesswallet.GetWalletCardsError 24 import android.service.quickaccesswallet.GetWalletCardsResponse 25 import android.service.quickaccesswallet.QuickAccessWalletClient 26 import android.service.quickaccesswallet.WalletCard 27 import android.util.Log 28 import com.android.systemui.R 29 import com.android.systemui.animation.Expandable 30 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 31 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 32 import com.android.systemui.common.shared.model.ContentDescription 33 import com.android.systemui.common.shared.model.Icon 34 import com.android.systemui.dagger.SysUISingleton 35 import com.android.systemui.dagger.qualifiers.Application 36 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.componentName 37 import com.android.systemui.plugins.ActivityStarter 38 import com.android.systemui.wallet.controller.QuickAccessWalletController 39 import javax.inject.Inject 40 import kotlinx.coroutines.channels.awaitClose 41 import kotlinx.coroutines.flow.Flow 42 import kotlinx.coroutines.suspendCancellableCoroutine 43 44 /** Quick access wallet quick affordance data source. */ 45 @SysUISingleton 46 class QuickAccessWalletKeyguardQuickAffordanceConfig 47 @Inject 48 constructor( 49 @Application private val context: Context, 50 private val walletController: QuickAccessWalletController, 51 private val activityStarter: ActivityStarter, 52 ) : KeyguardQuickAffordanceConfig { 53 54 override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET 55 56 override val pickerName: String = context.getString(R.string.accessibility_wallet_button) 57 58 override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen 59 60 override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = 61 conflatedCallbackFlow { 62 val callback = 63 object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback { 64 override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) { 65 val hasCards = response?.walletCards?.isNotEmpty() == true 66 trySendWithFailureLogging( 67 state( 68 isFeatureEnabled = walletController.isWalletEnabled, 69 hasCard = hasCards, 70 tileIcon = walletController.walletClient.tileIcon, 71 ), 72 TAG, 73 ) 74 } 75 76 override fun onWalletCardRetrievalError(error: GetWalletCardsError?) { 77 Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"") 78 trySendWithFailureLogging( 79 KeyguardQuickAffordanceConfig.LockScreenState.Hidden, 80 TAG, 81 ) 82 } 83 } 84 85 walletController.setupWalletChangeObservers( 86 callback, 87 QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, 88 QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE 89 ) 90 walletController.updateWalletPreference() 91 walletController.queryWalletCards(callback) 92 93 awaitClose { 94 walletController.unregisterWalletChangeObservers( 95 QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, 96 QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE 97 ) 98 } 99 } 100 101 override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { 102 return when { 103 !walletController.walletClient.isWalletServiceAvailable -> 104 KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice 105 !walletController.isWalletEnabled || queryCards().isEmpty() -> { 106 val componentName = 107 walletController.walletClient.createWalletSettingsIntent().toComponentName() 108 val actionText = 109 if (componentName != null) { 110 context.getString( 111 R.string.keyguard_affordance_enablement_dialog_action_template, 112 pickerName, 113 ) 114 } else { 115 null 116 } 117 KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( 118 instructions = 119 listOf( 120 context.getString( 121 R.string.keyguard_affordance_enablement_dialog_wallet_instruction_1 122 ), 123 context.getString( 124 R.string.keyguard_affordance_enablement_dialog_wallet_instruction_2 125 ), 126 ), 127 actionText = actionText, 128 actionComponentName = componentName, 129 ) 130 } 131 else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default() 132 } 133 } 134 135 override fun onTriggered( 136 expandable: Expandable?, 137 ): KeyguardQuickAffordanceConfig.OnTriggeredResult { 138 walletController.startQuickAccessUiIntent( 139 activityStarter, 140 expandable?.activityLaunchController(), 141 /* hasCard= */ true, 142 ) 143 return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 144 } 145 146 private suspend fun queryCards(): List<WalletCard> { 147 return suspendCancellableCoroutine { continuation -> 148 val callback = 149 object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback { 150 override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) { 151 continuation.resumeWith( 152 Result.success(response?.walletCards ?: emptyList()) 153 ) 154 } 155 156 override fun onWalletCardRetrievalError(error: GetWalletCardsError?) { 157 continuation.resumeWith(Result.success(emptyList())) 158 } 159 } 160 walletController.queryWalletCards(callback) 161 } 162 } 163 164 private fun state( 165 isFeatureEnabled: Boolean, 166 hasCard: Boolean, 167 tileIcon: Drawable?, 168 ): KeyguardQuickAffordanceConfig.LockScreenState { 169 return if (isFeatureEnabled && hasCard && tileIcon != null) { 170 KeyguardQuickAffordanceConfig.LockScreenState.Visible( 171 icon = 172 Icon.Loaded( 173 drawable = tileIcon, 174 contentDescription = 175 ContentDescription.Resource( 176 res = R.string.accessibility_wallet_button, 177 ), 178 ), 179 ) 180 } else { 181 KeyguardQuickAffordanceConfig.LockScreenState.Hidden 182 } 183 } 184 185 private fun Intent?.toComponentName(): String? { 186 if (this == null) { 187 return null 188 } 189 190 return componentName(packageName = `package`, action = action) 191 } 192 193 companion object { 194 private const val TAG = "QuickAccessWalletKeyguardQuickAffordanceConfig" 195 } 196 } 197