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.ui;
18 
19 import static android.provider.Settings.ACTION_LOCKSCREEN_SETTINGS;
20 
21 import android.content.Intent;
22 import android.graphics.drawable.Drawable;
23 import android.hardware.biometrics.BiometricSourceType;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.service.quickaccesswallet.QuickAccessWalletClient;
27 import android.service.quickaccesswallet.WalletServiceEvent;
28 import android.util.Log;
29 import android.view.Menu;
30 import android.view.MenuItem;
31 import android.view.Window;
32 import android.view.WindowManager;
33 import android.widget.Toolbar;
34 
35 import androidx.activity.ComponentActivity;
36 import androidx.annotation.NonNull;
37 import androidx.annotation.Nullable;
38 
39 import com.android.internal.logging.UiEventLogger;
40 import com.android.keyguard.KeyguardUpdateMonitor;
41 import com.android.keyguard.KeyguardUpdateMonitorCallback;
42 import com.android.settingslib.Utils;
43 import com.android.systemui.R;
44 import com.android.systemui.classifier.FalsingCollector;
45 import com.android.systemui.dagger.qualifiers.Background;
46 import com.android.systemui.dagger.qualifiers.Main;
47 import com.android.systemui.plugins.ActivityStarter;
48 import com.android.systemui.plugins.FalsingManager;
49 import com.android.systemui.settings.UserTracker;
50 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
51 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
52 import com.android.systemui.statusbar.policy.KeyguardStateController;
53 
54 import java.util.concurrent.Executor;
55 
56 import javax.inject.Inject;
57 
58 /**
59  * Displays Wallet carousel screen inside an activity.
60  */
61 public class WalletActivity extends ComponentActivity implements
62         QuickAccessWalletClient.WalletServiceEventListener {
63 
64     private static final String TAG = "WalletActivity";
65     private final KeyguardStateController mKeyguardStateController;
66     private final KeyguardDismissUtil mKeyguardDismissUtil;
67     private final ActivityStarter mActivityStarter;
68     private final Executor mExecutor;
69     private final Handler mHandler;
70     private final FalsingManager mFalsingManager;
71     private FalsingCollector mFalsingCollector;
72     private final UserTracker mUserTracker;
73     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
74     private final StatusBarKeyguardViewManager mKeyguardViewManager;
75     private final UiEventLogger mUiEventLogger;
76 
77     private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
78     private WalletScreenController mWalletScreenController;
79     private QuickAccessWalletClient mWalletClient;
80     private boolean mHasRegisteredListener;
81 
82     @Inject
WalletActivity( KeyguardStateController keyguardStateController, KeyguardDismissUtil keyguardDismissUtil, ActivityStarter activityStarter, @Background Executor executor, @Main Handler handler, FalsingManager falsingManager, FalsingCollector falsingCollector, UserTracker userTracker, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarKeyguardViewManager keyguardViewManager, UiEventLogger uiEventLogger)83     public WalletActivity(
84             KeyguardStateController keyguardStateController,
85             KeyguardDismissUtil keyguardDismissUtil,
86             ActivityStarter activityStarter,
87             @Background Executor executor,
88             @Main Handler handler,
89             FalsingManager falsingManager,
90             FalsingCollector falsingCollector,
91             UserTracker userTracker,
92             KeyguardUpdateMonitor keyguardUpdateMonitor,
93             StatusBarKeyguardViewManager keyguardViewManager,
94             UiEventLogger uiEventLogger) {
95         mKeyguardStateController = keyguardStateController;
96         mKeyguardDismissUtil = keyguardDismissUtil;
97         mActivityStarter = activityStarter;
98         mExecutor = executor;
99         mHandler = handler;
100         mFalsingManager = falsingManager;
101         mFalsingCollector = falsingCollector;
102         mUserTracker = userTracker;
103         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
104         mKeyguardViewManager = keyguardViewManager;
105         mUiEventLogger = uiEventLogger;
106     }
107 
108     @Override
onCreate(@ullable Bundle savedInstanceState)109     protected void onCreate(@Nullable Bundle savedInstanceState) {
110         super.onCreate(savedInstanceState);
111 
112         getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
113         requestWindowFeature(Window.FEATURE_NO_TITLE);
114         setContentView(R.layout.quick_access_wallet);
115 
116         Toolbar toolbar = findViewById(R.id.action_bar);
117         if (toolbar != null) {
118             setActionBar(toolbar);
119         }
120         getActionBar().setDisplayShowTitleEnabled(false);
121         getActionBar().setDisplayHomeAsUpEnabled(true);
122         getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
123         getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
124         WalletView walletView = requireViewById(R.id.wallet_view);
125 
126         mWalletClient = QuickAccessWalletClient.create(this, mExecutor);
127         mWalletScreenController = new WalletScreenController(
128                 this,
129                 walletView,
130                 mWalletClient,
131                 mActivityStarter,
132                 mExecutor,
133                 mHandler,
134                 mUserTracker,
135                 mFalsingManager,
136                 mKeyguardUpdateMonitor,
137                 mKeyguardStateController,
138                 mUiEventLogger);
139         mKeyguardUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
140             @Override
141             public void onBiometricRunningStateChanged(
142                     boolean running,
143                     BiometricSourceType biometricSourceType) {
144                 Log.d(TAG, "Biometric running state has changed.");
145                 mWalletScreenController.queryWalletCards();
146             }
147         };
148 
149         walletView.setFalsingCollector(mFalsingCollector);
150         walletView.setShowWalletAppOnClickListener(
151                 v -> {
152                     if (mWalletClient.createWalletIntent() == null) {
153                         Log.w(TAG, "Unable to create wallet app intent.");
154                         return;
155                     }
156                     if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
157                         return;
158                     }
159 
160                     if (mKeyguardStateController.isUnlocked()) {
161                         mUiEventLogger.log(WalletUiEvent.QAW_SHOW_ALL);
162                         mActivityStarter.startActivity(
163                                 mWalletClient.createWalletIntent(), true);
164                         finish();
165                     } else {
166                         mUiEventLogger.log(WalletUiEvent.QAW_UNLOCK_FROM_SHOW_ALL_BUTTON);
167                         mKeyguardDismissUtil.executeWhenUnlocked(() -> {
168                             mUiEventLogger.log(WalletUiEvent.QAW_SHOW_ALL);
169                             mActivityStarter.startActivity(
170                                     mWalletClient.createWalletIntent(), true);
171                             finish();
172                             return false;
173                         }, false, true);
174                     }
175                 });
176 
177         // Click the action button to re-render the screen when the device is unlocked.
178         walletView.setDeviceLockedActionOnClickListener(
179                 v -> {
180                     Log.d(TAG, "Wallet action button is clicked.");
181                     if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
182                         Log.d(TAG, "False tap detected on wallet action button.");
183                         return;
184                     }
185 
186                     mUiEventLogger.log(WalletUiEvent.QAW_UNLOCK_FROM_UNLOCK_BUTTON);
187                     mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false,
188                             false);
189                 });
190     }
191 
192     @Override
onStart()193     protected void onStart() {
194         super.onStart();
195         if (!mHasRegisteredListener) {
196             // Listener is registered even when device is locked. Should only be registered once.
197             mWalletClient.addWalletServiceEventListener(this);
198             mHasRegisteredListener = true;
199         }
200         mKeyguardStateController.addCallback(mWalletScreenController);
201         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
202     }
203 
204     @Override
onResume()205     protected void onResume() {
206         super.onResume();
207         mWalletScreenController.queryWalletCards();
208         mKeyguardViewManager.requestFp(
209                 true,
210                 Utils.getColorAttrDefaultColor(
211                         this, com.android.internal.R.attr.colorAccentPrimary));
212         mKeyguardViewManager.requestFace(true);
213     }
214 
215     @Override
onPause()216     protected void onPause() {
217         super.onPause();
218         mKeyguardViewManager.requestFp(false, -1);
219         mKeyguardViewManager.requestFace(false);
220     }
221 
222     @Override
onStop()223     protected void onStop() {
224         super.onStop();
225         finish();
226     }
227 
228     @Override
onCreateOptionsMenu(Menu menu)229     public boolean onCreateOptionsMenu(Menu menu) {
230         getMenuInflater().inflate(R.menu.wallet_activity_options_menu, menu);
231         return super.onCreateOptionsMenu(menu);
232     }
233 
234     /**
235      * Implements {@link QuickAccessWalletClient.WalletServiceEventListener}. Called when the wallet
236      * application propagates an event, such as an NFC tap, to the quick access wallet view.
237      */
238     @Override
onWalletServiceEvent(WalletServiceEvent event)239     public void onWalletServiceEvent(WalletServiceEvent event) {
240         switch (event.getEventType()) {
241             case WalletServiceEvent.TYPE_NFC_PAYMENT_STARTED:
242                 break;
243             case WalletServiceEvent.TYPE_WALLET_CARDS_UPDATED:
244                 mWalletScreenController.queryWalletCards();
245                 break;
246             default:
247                 Log.w(TAG, "onWalletServiceEvent: Unknown event type");
248         }
249     }
250 
251     @Override
onOptionsItemSelected(@onNull MenuItem item)252     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
253         int itemId = item.getItemId();
254         if (itemId == android.R.id.home) {
255             finish();
256             return true;
257         } else if (itemId == R.id.wallet_lockscreen_settings) {
258             Intent intent =
259                     new Intent(ACTION_LOCKSCREEN_SETTINGS)
260                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
261             mActivityStarter.startActivity(intent, true);
262             return true;
263         }
264         return super.onOptionsItemSelected(item);
265     }
266 
267     @Override
onDestroy()268     protected void onDestroy() {
269         mKeyguardStateController.removeCallback(mWalletScreenController);
270         if (mKeyguardUpdateMonitorCallback != null) {
271             mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
272         }
273         mWalletScreenController.onDismissed();
274         mWalletClient.removeWalletServiceEventListener(this);
275         mHasRegisteredListener = false;
276         super.onDestroy();
277     }
278 
getHomeIndicatorDrawable()279     private Drawable getHomeIndicatorDrawable() {
280         Drawable drawable = getDrawable(R.drawable.ic_close);
281         drawable.setTint(getColor(R.color.material_dynamic_neutral70));
282         return drawable;
283     }
284 }
285