• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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