• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.systemui.wallet.controller;
18 
19 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
20 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
21 
22 import android.app.PendingIntent;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.database.ContentObserver;
26 import android.os.UserHandle;
27 import android.provider.Settings;
28 import android.service.quickaccesswallet.GetWalletCardsRequest;
29 import android.service.quickaccesswallet.QuickAccessWalletClient;
30 import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
31 import android.util.Log;
32 
33 import com.android.systemui.R;
34 import com.android.systemui.animation.ActivityLaunchAnimator;
35 import com.android.systemui.dagger.SysUISingleton;
36 import com.android.systemui.dagger.qualifiers.Background;
37 import com.android.systemui.dagger.qualifiers.Main;
38 import com.android.systemui.plugins.ActivityStarter;
39 import com.android.systemui.util.settings.SecureSettings;
40 import com.android.systemui.util.time.SystemClock;
41 import com.android.systemui.wallet.ui.WalletActivity;
42 
43 import java.util.concurrent.Executor;
44 import java.util.concurrent.TimeUnit;
45 
46 import javax.inject.Inject;
47 
48 /**
49  * Controller to handle communication between SystemUI and Quick Access Wallet Client.
50  */
51 @SysUISingleton
52 public class QuickAccessWalletController {
53 
54     /**
55      * Event for the wallet status change, e.g. the default payment app change and the wallet
56      * preference change.
57      */
58     public enum WalletChangeEvent {
59         DEFAULT_PAYMENT_APP_CHANGE,
60         WALLET_PREFERENCE_CHANGE,
61     }
62 
63     private static final String TAG = "QAWController";
64     private static final long RECREATION_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10L);
65     private final Context mContext;
66     private final Executor mExecutor;
67     private final Executor mBgExecutor;
68     private final SecureSettings mSecureSettings;
69     private final SystemClock mClock;
70 
71     private QuickAccessWalletClient mQuickAccessWalletClient;
72     private ContentObserver mWalletPreferenceObserver;
73     private ContentObserver mDefaultPaymentAppObserver;
74     private int mWalletPreferenceChangeEvents = 0;
75     private int mDefaultPaymentAppChangeEvents = 0;
76     private boolean mWalletEnabled = false;
77     private long mQawClientCreatedTimeMillis;
78 
79     @Inject
QuickAccessWalletController( Context context, @Main Executor executor, @Background Executor bgExecutor, SecureSettings secureSettings, QuickAccessWalletClient quickAccessWalletClient, SystemClock clock)80     public QuickAccessWalletController(
81             Context context,
82             @Main Executor executor,
83             @Background Executor bgExecutor,
84             SecureSettings secureSettings,
85             QuickAccessWalletClient quickAccessWalletClient,
86             SystemClock clock) {
87         mContext = context;
88         mExecutor = executor;
89         mBgExecutor = bgExecutor;
90         mSecureSettings = secureSettings;
91         mQuickAccessWalletClient = quickAccessWalletClient;
92         mClock = clock;
93         mQawClientCreatedTimeMillis = mClock.elapsedRealtime();
94     }
95 
96     /**
97      * Returns true if the Quick Access Wallet service & feature is available.
98      */
isWalletEnabled()99     public boolean isWalletEnabled() {
100         return mWalletEnabled;
101     }
102 
103     /**
104      * Returns the current instance of {@link QuickAccessWalletClient} in the controller.
105      */
getWalletClient()106     public QuickAccessWalletClient getWalletClient() {
107         return mQuickAccessWalletClient;
108     }
109 
110     /**
111      * Setup the wallet change observers per {@link WalletChangeEvent}
112      *
113      * @param cardsRetriever a callback that retrieves the wallet cards
114      * @param events {@link WalletChangeEvent} need to be handled.
115      */
setupWalletChangeObservers( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, WalletChangeEvent... events)116     public void setupWalletChangeObservers(
117             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever,
118             WalletChangeEvent... events) {
119         for (WalletChangeEvent event : events) {
120             if (event == WALLET_PREFERENCE_CHANGE) {
121                 setupWalletPreferenceObserver();
122             } else if (event == DEFAULT_PAYMENT_APP_CHANGE) {
123                 setupDefaultPaymentAppObserver(cardsRetriever);
124             }
125         }
126     }
127 
128     /**
129      * Unregister wallet change observers per {@link WalletChangeEvent} if needed.
130      */
unregisterWalletChangeObservers(WalletChangeEvent... events)131     public void unregisterWalletChangeObservers(WalletChangeEvent... events) {
132         for (WalletChangeEvent event : events) {
133             if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) {
134                 mWalletPreferenceChangeEvents--;
135                 if (mWalletPreferenceChangeEvents == 0) {
136                     mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
137                 }
138             } else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) {
139                 mDefaultPaymentAppChangeEvents--;
140                 if (mDefaultPaymentAppChangeEvents == 0) {
141                     mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver);
142                 }
143             }
144         }
145     }
146 
147     /**
148      * Update the "show wallet" preference.
149      */
updateWalletPreference()150     public void updateWalletPreference() {
151         mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable()
152                 && mQuickAccessWalletClient.isWalletFeatureAvailable()
153                 && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked();
154     }
155 
156     /**
157      * Query the wallet cards from {@link QuickAccessWalletClient}.
158      *
159      * @param cardsRetriever a callback to retrieve wallet cards.
160      */
queryWalletCards( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever)161     public void queryWalletCards(
162             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
163         if (mClock.elapsedRealtime() - mQawClientCreatedTimeMillis
164                 > RECREATION_TIME_WINDOW) {
165             Log.i(TAG, "Re-creating the QAW client to avoid stale.");
166             reCreateWalletClient();
167         }
168         if (!mQuickAccessWalletClient.isWalletFeatureAvailable()) {
169             Log.d(TAG, "QuickAccessWallet feature is not available.");
170             return;
171         }
172         int cardWidth =
173                 mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width);
174         int cardHeight =
175                 mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height);
176         int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
177         GetWalletCardsRequest request =
178                 new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
179         mQuickAccessWalletClient.getWalletCards(mBgExecutor, request, cardsRetriever);
180     }
181 
182     /**
183      * Re-create the {@link QuickAccessWalletClient} of the controller.
184      */
reCreateWalletClient()185     public void reCreateWalletClient() {
186         mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext, mBgExecutor);
187         mQawClientCreatedTimeMillis = mClock.elapsedRealtime();
188     }
189 
190     /**
191      * Starts the QuickAccessWallet UI: either the app's designated UI, or the built-in Wallet UI.
192      *
193      * If the service has configured itself so that
194      * {@link QuickAccessWalletClient#useTargetActivityForQuickAccess()}
195      * is true, or the service isn't providing any cards, use the target activity. Otherwise, use
196      * the SysUi {@link WalletActivity}
197      *
198      * The Wallet target activity is defined as the {@link android.app.PendingIntent} returned by
199      * {@link QuickAccessWalletClient#getWalletPendingIntent} if that is not null. If that is null,
200      * then the {@link Intent} returned by {@link QuickAccessWalletClient#createWalletIntent()}. If
201      * that too is null, then fall back to {@link WalletActivity}.
202      *
203      * @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent.
204      * @param animationController an {@link ActivityLaunchAnimator.Controller} to provide a
205      *                            smooth animation for the activity launch.
206      * @param hasCard whether the service returns any cards.
207      */
startQuickAccessUiIntent(ActivityStarter activityStarter, ActivityLaunchAnimator.Controller animationController, boolean hasCard)208     public void startQuickAccessUiIntent(ActivityStarter activityStarter,
209             ActivityLaunchAnimator.Controller animationController,
210             boolean hasCard) {
211         mQuickAccessWalletClient.getWalletPendingIntent(mExecutor,
212                 walletPendingIntent -> {
213                     if (walletPendingIntent != null) {
214                         startQuickAccessViaPendingIntent(walletPendingIntent, activityStarter,
215                                 animationController);
216                         return;
217                     }
218                     Intent intent = null;
219                     if (!hasCard) {
220                         intent = mQuickAccessWalletClient.createWalletIntent();
221                     }
222                     if (intent == null) {
223                         intent = getSysUiWalletIntent();
224                     }
225                     startQuickAccessViaIntent(intent, hasCard, activityStarter,
226                             animationController);
227 
228                 });
229     }
230 
getSysUiWalletIntent()231     private Intent getSysUiWalletIntent() {
232         return new Intent(mContext, WalletActivity.class)
233                 .setAction(Intent.ACTION_VIEW);
234     }
235 
startQuickAccessViaIntent(Intent intent, boolean hasCard, ActivityStarter activityStarter, ActivityLaunchAnimator.Controller animationController)236     private void startQuickAccessViaIntent(Intent intent,
237             boolean hasCard,
238             ActivityStarter activityStarter,
239             ActivityLaunchAnimator.Controller animationController) {
240         if (hasCard) {
241             activityStarter.startActivity(intent, true /* dismissShade */,
242                     animationController, true /* showOverLockscreenWhenLocked */);
243         } else {
244             activityStarter.postStartActivityDismissingKeyguard(
245                     intent,
246                     /* delay= */ 0,
247                     animationController);
248         }
249     }
250 
startQuickAccessViaPendingIntent(PendingIntent pendingIntent, ActivityStarter activityStarter, ActivityLaunchAnimator.Controller animationController)251     private void startQuickAccessViaPendingIntent(PendingIntent pendingIntent,
252             ActivityStarter activityStarter,
253             ActivityLaunchAnimator.Controller animationController) {
254         activityStarter.postStartActivityDismissingKeyguard(
255                 pendingIntent,
256                 animationController);
257 
258     }
259 
260 
setupDefaultPaymentAppObserver( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever)261     private void setupDefaultPaymentAppObserver(
262             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
263         if (mDefaultPaymentAppObserver == null) {
264             mDefaultPaymentAppObserver = new ContentObserver(null /* handler */) {
265                 @Override
266                 public void onChange(boolean selfChange) {
267                     mExecutor.execute(() -> {
268                         reCreateWalletClient();
269                         updateWalletPreference();
270                         queryWalletCards(cardsRetriever);
271                     });
272                 }
273             };
274 
275             mSecureSettings.registerContentObserverForUser(
276                     Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
277                     false /* notifyForDescendants */,
278                     mDefaultPaymentAppObserver,
279                     UserHandle.USER_ALL);
280         }
281         mDefaultPaymentAppChangeEvents++;
282     }
283 
setupWalletPreferenceObserver()284     private void setupWalletPreferenceObserver() {
285         if (mWalletPreferenceObserver == null) {
286             mWalletPreferenceObserver = new ContentObserver(null /* handler */) {
287                 @Override
288                 public void onChange(boolean selfChange) {
289                     mExecutor.execute(() -> {
290                         updateWalletPreference();
291                     });
292                 }
293             };
294 
295             mSecureSettings.registerContentObserverForUser(
296                     QuickAccessWalletClientImpl.SETTING_KEY,
297                     false /* notifyForDescendants */,
298                     mWalletPreferenceObserver,
299                     UserHandle.USER_ALL);
300         }
301         mWalletPreferenceChangeEvents++;
302     }
303 }
304