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