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.content.Context; 23 import android.database.ContentObserver; 24 import android.provider.Settings; 25 import android.service.quickaccesswallet.GetWalletCardsRequest; 26 import android.service.quickaccesswallet.QuickAccessWalletClient; 27 import android.service.quickaccesswallet.QuickAccessWalletClientImpl; 28 import android.util.Log; 29 30 import com.android.systemui.R; 31 import com.android.systemui.dagger.SysUISingleton; 32 import com.android.systemui.dagger.qualifiers.Main; 33 import com.android.systemui.util.settings.SecureSettings; 34 import com.android.systemui.util.time.SystemClock; 35 36 import java.util.concurrent.Executor; 37 import java.util.concurrent.TimeUnit; 38 39 import javax.inject.Inject; 40 41 /** 42 * Controller to handle communication between SystemUI and Quick Access Wallet Client. 43 */ 44 @SysUISingleton 45 public class QuickAccessWalletController { 46 47 /** 48 * Event for the wallet status change, e.g. the default payment app change and the wallet 49 * preference change. 50 */ 51 public enum WalletChangeEvent { 52 DEFAULT_PAYMENT_APP_CHANGE, 53 WALLET_PREFERENCE_CHANGE, 54 } 55 56 private static final String TAG = "QAWController"; 57 private static final long RECREATION_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10L); 58 private final Context mContext; 59 private final Executor mExecutor; 60 private final SecureSettings mSecureSettings; 61 private final SystemClock mClock; 62 63 private QuickAccessWalletClient mQuickAccessWalletClient; 64 private ContentObserver mWalletPreferenceObserver; 65 private ContentObserver mDefaultPaymentAppObserver; 66 private int mWalletPreferenceChangeEvents = 0; 67 private int mDefaultPaymentAppChangeEvents = 0; 68 private boolean mWalletEnabled = false; 69 private long mQawClientCreatedTimeMillis; 70 71 @Inject QuickAccessWalletController( Context context, @Main Executor executor, SecureSettings secureSettings, QuickAccessWalletClient quickAccessWalletClient, SystemClock clock)72 public QuickAccessWalletController( 73 Context context, 74 @Main Executor executor, 75 SecureSettings secureSettings, 76 QuickAccessWalletClient quickAccessWalletClient, 77 SystemClock clock) { 78 mContext = context; 79 mExecutor = executor; 80 mSecureSettings = secureSettings; 81 mQuickAccessWalletClient = quickAccessWalletClient; 82 mClock = clock; 83 mQawClientCreatedTimeMillis = mClock.elapsedRealtime(); 84 } 85 86 /** 87 * Returns true if the Quick Access Wallet service & feature is available. 88 */ isWalletEnabled()89 public boolean isWalletEnabled() { 90 return mWalletEnabled; 91 } 92 93 /** 94 * Returns the current instance of {@link QuickAccessWalletClient} in the controller. 95 */ getWalletClient()96 public QuickAccessWalletClient getWalletClient() { 97 return mQuickAccessWalletClient; 98 } 99 100 /** 101 * Setup the wallet change observers per {@link WalletChangeEvent} 102 * 103 * @param cardsRetriever a callback that retrieves the wallet cards 104 * @param events {@link WalletChangeEvent} need to be handled. 105 */ setupWalletChangeObservers( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, WalletChangeEvent... events)106 public void setupWalletChangeObservers( 107 QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, 108 WalletChangeEvent... events) { 109 for (WalletChangeEvent event : events) { 110 if (event == WALLET_PREFERENCE_CHANGE) { 111 setupWalletPreferenceObserver(); 112 } else if (event == DEFAULT_PAYMENT_APP_CHANGE) { 113 setupDefaultPaymentAppObserver(cardsRetriever); 114 } 115 } 116 } 117 118 /** 119 * Unregister wallet change observers per {@link WalletChangeEvent} if needed. 120 * 121 */ unregisterWalletChangeObservers(WalletChangeEvent... events)122 public void unregisterWalletChangeObservers(WalletChangeEvent... events) { 123 for (WalletChangeEvent event : events) { 124 if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) { 125 mWalletPreferenceChangeEvents--; 126 if (mWalletPreferenceChangeEvents == 0) { 127 mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver); 128 } 129 } else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) { 130 mDefaultPaymentAppChangeEvents--; 131 if (mDefaultPaymentAppChangeEvents == 0) { 132 mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver); 133 } 134 } 135 } 136 } 137 138 /** 139 * Update the "show wallet" preference. 140 */ updateWalletPreference()141 public void updateWalletPreference() { 142 mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable() 143 && mQuickAccessWalletClient.isWalletFeatureAvailable() 144 && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked(); 145 } 146 147 /** 148 * Query the wallet cards from {@link QuickAccessWalletClient}. 149 * 150 * @param cardsRetriever a callback to retrieve wallet cards. 151 */ queryWalletCards( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever)152 public void queryWalletCards( 153 QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { 154 if (mClock.elapsedRealtime() - mQawClientCreatedTimeMillis 155 > RECREATION_TIME_WINDOW) { 156 Log.i(TAG, "Re-creating the QAW client to avoid stale."); 157 reCreateWalletClient(); 158 } 159 if (!mQuickAccessWalletClient.isWalletFeatureAvailable()) { 160 Log.d(TAG, "QuickAccessWallet feature is not available."); 161 return; 162 } 163 int cardWidth = 164 mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width); 165 int cardHeight = 166 mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height); 167 int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); 168 GetWalletCardsRequest request = 169 new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); 170 mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever); 171 } 172 173 /** 174 * Re-create the {@link QuickAccessWalletClient} of the controller. 175 */ reCreateWalletClient()176 public void reCreateWalletClient() { 177 mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext); 178 mQawClientCreatedTimeMillis = mClock.elapsedRealtime(); 179 } 180 setupDefaultPaymentAppObserver( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever)181 private void setupDefaultPaymentAppObserver( 182 QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { 183 if (mDefaultPaymentAppObserver == null) { 184 mDefaultPaymentAppObserver = new ContentObserver(null /* handler */) { 185 @Override 186 public void onChange(boolean selfChange) { 187 mExecutor.execute(() -> { 188 reCreateWalletClient(); 189 updateWalletPreference(); 190 queryWalletCards(cardsRetriever); 191 }); 192 } 193 }; 194 195 mSecureSettings.registerContentObserver( 196 Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), 197 false /* notifyForDescendants */, 198 mDefaultPaymentAppObserver); 199 } 200 mDefaultPaymentAppChangeEvents++; 201 } 202 setupWalletPreferenceObserver()203 private void setupWalletPreferenceObserver() { 204 if (mWalletPreferenceObserver == null) { 205 mWalletPreferenceObserver = new ContentObserver(null /* handler */) { 206 @Override 207 public void onChange(boolean selfChange) { 208 mExecutor.execute(() -> { 209 updateWalletPreference(); 210 }); 211 } 212 }; 213 214 mSecureSettings.registerContentObserver( 215 Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY), 216 false /* notifyForDescendants */, 217 mWalletPreferenceObserver); 218 } 219 mWalletPreferenceChangeEvents++; 220 } 221 } 222