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.biometrics;
18 
19 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
20 
21 import android.annotation.NonNull;
22 import android.content.res.Configuration;
23 import android.util.MathUtils;
24 import android.view.MotionEvent;
25 
26 import com.android.keyguard.KeyguardUpdateMonitor;
27 import com.android.systemui.R;
28 import com.android.systemui.dump.DumpManager;
29 import com.android.systemui.plugins.statusbar.StatusBarStateController;
30 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
31 import com.android.systemui.statusbar.StatusBarState;
32 import com.android.systemui.statusbar.phone.KeyguardBouncer;
33 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
34 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
35 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
36 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
37 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
38 import com.android.systemui.statusbar.policy.ConfigurationController;
39 import com.android.systemui.statusbar.policy.KeyguardStateController;
40 import com.android.systemui.util.time.SystemClock;
41 
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 
45 /**
46  * Class that coordinates non-HBM animations during keyguard authentication.
47  */
48 public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
49     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
50     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
51     @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
52     @NonNull private final ConfigurationController mConfigurationController;
53     @NonNull private final SystemClock mSystemClock;
54     @NonNull private final KeyguardStateController mKeyguardStateController;
55     @NonNull private final UdfpsController mUdfpsController;
56     @NonNull private final UnlockedScreenOffAnimationController
57             mUnlockedScreenOffAnimationController;
58 
59     private boolean mShowingUdfpsBouncer;
60     private boolean mUdfpsRequested;
61     private boolean mQsExpanded;
62     private boolean mFaceDetectRunning;
63     private int mStatusBarState;
64     private float mTransitionToFullShadeProgress;
65     private float mLastDozeAmount;
66     private long mLastUdfpsBouncerShowTime = -1;
67     private float mStatusBarExpansion;
68     private boolean mLaunchTransitionFadingAway;
69 
70     /**
71      * hidden amount of pin/pattern/password bouncer
72      * {@link KeyguardBouncer#EXPANSION_VISIBLE} (0f) to
73      * {@link KeyguardBouncer#EXPANSION_HIDDEN} (1f)
74      */
75     private float mInputBouncerHiddenAmount;
76     private boolean mIsBouncerVisible;
77 
UdfpsKeyguardViewController( @onNull UdfpsKeyguardView view, @NonNull StatusBarStateController statusBarStateController, @NonNull PanelExpansionStateManager panelExpansionStateManager, @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, @NonNull DumpManager dumpManager, @NonNull LockscreenShadeTransitionController transitionController, @NonNull ConfigurationController configurationController, @NonNull SystemClock systemClock, @NonNull KeyguardStateController keyguardStateController, @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, @NonNull SystemUIDialogManager systemUIDialogManager, @NonNull UdfpsController udfpsController)78     protected UdfpsKeyguardViewController(
79             @NonNull UdfpsKeyguardView view,
80             @NonNull StatusBarStateController statusBarStateController,
81             @NonNull PanelExpansionStateManager panelExpansionStateManager,
82             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
83             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
84             @NonNull DumpManager dumpManager,
85             @NonNull LockscreenShadeTransitionController transitionController,
86             @NonNull ConfigurationController configurationController,
87             @NonNull SystemClock systemClock,
88             @NonNull KeyguardStateController keyguardStateController,
89             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
90             @NonNull SystemUIDialogManager systemUIDialogManager,
91             @NonNull UdfpsController udfpsController) {
92         super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
93                 dumpManager);
94         mKeyguardViewManager = statusBarKeyguardViewManager;
95         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
96         mLockScreenShadeTransitionController = transitionController;
97         mConfigurationController = configurationController;
98         mSystemClock = systemClock;
99         mKeyguardStateController = keyguardStateController;
100         mUdfpsController = udfpsController;
101         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
102     }
103 
104     @Override
getTag()105     @NonNull String getTag() {
106         return "UdfpsKeyguardViewController";
107     }
108 
109     @Override
onInit()110     public void onInit() {
111         super.onInit();
112         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
113     }
114 
115     @Override
onViewAttached()116     protected void onViewAttached() {
117         super.onViewAttached();
118         final float dozeAmount = mStatusBarStateController.getDozeAmount();
119         mLastDozeAmount = dozeAmount;
120         mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
121         mStatusBarStateController.addCallback(mStateListener);
122 
123         mUdfpsRequested = false;
124 
125         mLaunchTransitionFadingAway = mKeyguardStateController.isLaunchTransitionFadingAway();
126         mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
127         mStatusBarState = mStatusBarStateController.getState();
128         mQsExpanded = mKeyguardViewManager.isQsExpanded();
129         mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
130         mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
131         mConfigurationController.addCallback(mConfigurationListener);
132         mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
133         updateAlpha();
134         updatePauseAuth();
135 
136         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
137         mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
138         mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
139     }
140 
141     @Override
onViewDetached()142     protected void onViewDetached() {
143         super.onViewDetached();
144         mFaceDetectRunning = false;
145 
146         mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback);
147         mStatusBarStateController.removeCallback(mStateListener);
148         mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
149         mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
150         mConfigurationController.removeCallback(mConfigurationListener);
151         mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
152         if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
153             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
154         }
155         mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
156     }
157 
158     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)159     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
160         super.dump(fd, pw, args);
161         pw.println("mShowingUdfpsBouncer=" + mShowingUdfpsBouncer);
162         pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
163         pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState));
164         pw.println("mQsExpanded=" + mQsExpanded);
165         pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
166         pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
167         pw.println("mStatusBarExpansion=" + mStatusBarExpansion);
168         pw.println("unpausedAlpha=" + mView.getUnpausedAlpha());
169         pw.println("mUdfpsRequested=" + mUdfpsRequested);
170         pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
171         pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway);
172     }
173 
174     /**
175      * Overrides non-bouncer show logic in shouldPauseAuth to still show icon.
176      * @return whether the udfpsBouncer has been newly shown or hidden
177      */
showUdfpsBouncer(boolean show)178     private boolean showUdfpsBouncer(boolean show) {
179         if (mShowingUdfpsBouncer == show) {
180             return false;
181         }
182 
183         boolean udfpsAffordanceWasNotShowing = shouldPauseAuth();
184         mShowingUdfpsBouncer = show;
185         if (mShowingUdfpsBouncer) {
186             mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
187         }
188         if (mShowingUdfpsBouncer) {
189             if (udfpsAffordanceWasNotShowing) {
190                 mView.animateInUdfpsBouncer(null);
191             }
192 
193             if (mKeyguardViewManager.isOccluded()) {
194                 mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
195             }
196 
197             mView.announceForAccessibility(mView.getContext().getString(
198                     R.string.accessibility_fingerprint_bouncer));
199         } else {
200             mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
201         }
202         updateAlpha();
203         updatePauseAuth();
204         return true;
205     }
206 
207     /**
208      * Returns true if the fingerprint manager is running but we want to temporarily pause
209      * authentication. On the keyguard, we may want to show udfps when the shade
210      * is expanded, so this can be overridden with the showBouncer method.
211      */
shouldPauseAuth()212     public boolean shouldPauseAuth() {
213         if (mShowingUdfpsBouncer) {
214             return false;
215         }
216 
217         if (mUdfpsRequested && !mNotificationShadeVisible
218                 && (!mIsBouncerVisible
219                 || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)) {
220             return false;
221         }
222 
223         if (mDialogManager.shouldHideAffordance()) {
224             return true;
225         }
226 
227         if (mLaunchTransitionFadingAway) {
228             return true;
229         }
230 
231         if (mStatusBarState != KEYGUARD) {
232             return true;
233         }
234 
235         if (mQsExpanded) {
236             return true;
237         }
238 
239         if (mInputBouncerHiddenAmount < .5f || mIsBouncerVisible) {
240             return true;
241         }
242 
243         return false;
244     }
245 
246     @Override
listenForTouchesOutsideView()247     public boolean listenForTouchesOutsideView() {
248         return true;
249     }
250 
251     @Override
onTouchOutsideView()252     public void onTouchOutsideView() {
253         maybeShowInputBouncer();
254     }
255 
256     /**
257      * If we were previously showing the udfps bouncer, hide it and instead show the regular
258      * (pin/pattern/password) bouncer.
259      *
260      * Does nothing if we weren't previously showing the UDFPS bouncer.
261      */
maybeShowInputBouncer()262     private void maybeShowInputBouncer() {
263         if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
264             mKeyguardViewManager.showBouncer(true);
265         }
266     }
267 
268     /**
269      * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside
270      * of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password
271      * bouncer.
272      */
hasUdfpsBouncerShownWithMinTime()273     private boolean hasUdfpsBouncerShownWithMinTime() {
274         return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 200;
275     }
276 
277     /**
278      * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
279      * transitioning yet, while 1.0f means we've fully dragged down.
280      */
setTransitionToFullShadeProgress(float progress)281     public void setTransitionToFullShadeProgress(float progress) {
282         mTransitionToFullShadeProgress = progress;
283         updateAlpha();
284     }
285 
updateAlpha()286     private void updateAlpha() {
287         // fade icon on transitions to showing the status bar, but if mUdfpsRequested, then
288         // the keyguard is occluded by some application - so instead use the input bouncer
289         // hidden amount to determine the fade
290         float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mStatusBarExpansion;
291         int alpha = mShowingUdfpsBouncer ? 255
292                 : (int) MathUtils.constrain(
293                     MathUtils.map(.5f, .9f, 0f, 255f, expansion),
294                     0f, 255f);
295         if (!mShowingUdfpsBouncer) {
296             alpha *= (1.0f - mTransitionToFullShadeProgress);
297         }
298         mView.setUnpausedAlpha(alpha);
299     }
300 
301     private final StatusBarStateController.StateListener mStateListener =
302             new StatusBarStateController.StateListener() {
303         @Override
304         public void onDozeAmountChanged(float linear, float eased) {
305             if (mLastDozeAmount < linear) {
306                 showUdfpsBouncer(false);
307             }
308             mView.onDozeAmountChanged(linear, eased);
309             mLastDozeAmount = linear;
310             updatePauseAuth();
311         }
312 
313         @Override
314         public void onStateChanged(int statusBarState) {
315             mStatusBarState = statusBarState;
316             mView.setStatusBarState(statusBarState);
317             updatePauseAuth();
318         }
319     };
320 
321     private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor =
322             new StatusBarKeyguardViewManager.AlternateAuthInterceptor() {
323                 @Override
324                 public boolean showAlternateAuthBouncer() {
325                     return showUdfpsBouncer(true);
326                 }
327 
328                 @Override
329                 public boolean hideAlternateAuthBouncer() {
330                     return showUdfpsBouncer(false);
331                 }
332 
333                 @Override
334                 public boolean isShowingAlternateAuthBouncer() {
335                     return mShowingUdfpsBouncer;
336                 }
337 
338                 @Override
339                 public void requestUdfps(boolean request, int color) {
340                     mUdfpsRequested = request;
341                     mView.requestUdfps(request, color);
342                     updateAlpha();
343                     updatePauseAuth();
344                 }
345 
346                 @Override
347                 public boolean isAnimating() {
348                     return false;
349                 }
350 
351                 @Override
352                 public void setQsExpanded(boolean expanded) {
353                     mQsExpanded = expanded;
354                     updatePauseAuth();
355                 }
356 
357                 @Override
358                 public boolean onTouch(MotionEvent event) {
359                     if (mTransitionToFullShadeProgress != 0) {
360                         return false;
361                     }
362                     return mUdfpsController.onTouch(event);
363                 }
364 
365                 @Override
366                 public void setBouncerExpansionChanged(float expansion) {
367                     mInputBouncerHiddenAmount = expansion;
368                     updateAlpha();
369                     updatePauseAuth();
370                 }
371 
372                 @Override
373                 public void onBouncerVisibilityChanged() {
374                     mIsBouncerVisible = mKeyguardViewManager.isBouncerShowing();
375                     if (!mIsBouncerVisible) {
376                         mInputBouncerHiddenAmount = 1f;
377                     } else if (mKeyguardViewManager.isBouncerShowing()) {
378                         mInputBouncerHiddenAmount = 0f;
379                     }
380                     updateAlpha();
381                     updatePauseAuth();
382                 }
383 
384                 @Override
385                 public void dump(PrintWriter pw) {
386                     pw.println(getTag());
387                 }
388             };
389 
390     private final ConfigurationController.ConfigurationListener mConfigurationListener =
391             new ConfigurationController.ConfigurationListener() {
392                 @Override
393                 public void onUiModeChanged() {
394                     mView.updateColor();
395                 }
396 
397                 @Override
398                 public void onThemeChanged() {
399                     mView.updateColor();
400                 }
401 
402                 @Override
403                 public void onConfigChanged(Configuration newConfig) {
404                     mView.updateColor();
405                 }
406             };
407 
408     private final PanelExpansionListener mPanelExpansionListener = new PanelExpansionListener() {
409         @Override
410         public void onPanelExpansionChanged(
411                 float fraction, boolean expanded, boolean tracking) {
412             mStatusBarExpansion = fraction;
413             updateAlpha();
414         }
415     };
416 
417     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
418             new KeyguardStateController.Callback() {
419                 @Override
420                 public void onLaunchTransitionFadingAwayChanged() {
421                     mLaunchTransitionFadingAway =
422                             mKeyguardStateController.isLaunchTransitionFadingAway();
423                     updatePauseAuth();
424                 }
425             };
426 
427     private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
428             (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
429 }
430