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