1 /* 2 * Copyright (C) 2020 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.keyguard; 18 19 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; 20 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; 21 22 import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; 23 import static com.android.keyguard.LockIconView.ICON_LOCK; 24 import static com.android.keyguard.LockIconView.ICON_UNLOCK; 25 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; 26 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; 27 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; 28 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; 29 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; 30 31 import android.content.res.Configuration; 32 import android.content.res.Resources; 33 import android.graphics.Point; 34 import android.graphics.Rect; 35 import android.graphics.drawable.AnimatedStateListDrawable; 36 import android.hardware.biometrics.BiometricAuthenticator; 37 import android.hardware.biometrics.BiometricSourceType; 38 import android.os.Process; 39 import android.os.VibrationAttributes; 40 import android.util.DisplayMetrics; 41 import android.util.Log; 42 import android.util.MathUtils; 43 import android.view.HapticFeedbackConstants; 44 import android.view.MotionEvent; 45 import android.view.VelocityTracker; 46 import android.view.View; 47 import android.view.WindowManager; 48 import android.view.accessibility.AccessibilityManager; 49 import android.view.accessibility.AccessibilityNodeInfo; 50 51 import androidx.annotation.NonNull; 52 import androidx.annotation.Nullable; 53 import androidx.annotation.VisibleForTesting; 54 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 55 56 import com.android.settingslib.udfps.UdfpsOverlayParams; 57 import com.android.systemui.Dumpable; 58 import com.android.systemui.R; 59 import com.android.systemui.biometrics.AuthController; 60 import com.android.systemui.biometrics.AuthRippleController; 61 import com.android.systemui.biometrics.UdfpsController; 62 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; 63 import com.android.systemui.dagger.SysUISingleton; 64 import com.android.systemui.dagger.qualifiers.Main; 65 import com.android.systemui.dump.DumpManager; 66 import com.android.systemui.flags.FeatureFlags; 67 import com.android.systemui.flags.Flags; 68 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; 69 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; 70 import com.android.systemui.keyguard.shared.model.TransitionStep; 71 import com.android.systemui.plugins.FalsingManager; 72 import com.android.systemui.plugins.statusbar.StatusBarStateController; 73 import com.android.systemui.statusbar.StatusBarState; 74 import com.android.systemui.statusbar.VibratorHelper; 75 import com.android.systemui.statusbar.policy.ConfigurationController; 76 import com.android.systemui.statusbar.policy.KeyguardStateController; 77 import com.android.systemui.util.ViewController; 78 import com.android.systemui.util.concurrency.DelayableExecutor; 79 80 import java.io.PrintWriter; 81 import java.util.Objects; 82 import java.util.function.Consumer; 83 84 import javax.inject.Inject; 85 86 /** 87 * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen. 88 * 89 * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock 90 * icon will show a set distance from the bottom of the device. 91 */ 92 @SysUISingleton 93 public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { 94 private static final String TAG = "LockIconViewController"; 95 private static final float sDefaultDensity = 96 (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT; 97 private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36); 98 private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = 99 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); 100 101 private final long mLongPressTimeout; 102 @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 103 @NonNull private final KeyguardViewController mKeyguardViewController; 104 @NonNull private final StatusBarStateController mStatusBarStateController; 105 @NonNull private final KeyguardStateController mKeyguardStateController; 106 @NonNull private final FalsingManager mFalsingManager; 107 @NonNull private final AuthController mAuthController; 108 @NonNull private final AccessibilityManager mAccessibilityManager; 109 @NonNull private final ConfigurationController mConfigurationController; 110 @NonNull private final DelayableExecutor mExecutor; 111 private boolean mUdfpsEnrolled; 112 113 @NonNull private final AnimatedStateListDrawable mIcon; 114 115 @NonNull private CharSequence mUnlockedLabel; 116 @NonNull private CharSequence mLockedLabel; 117 @NonNull private final VibratorHelper mVibrator; 118 @Nullable private final AuthRippleController mAuthRippleController; 119 @NonNull private final FeatureFlags mFeatureFlags; 120 @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; 121 @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; 122 @NonNull private final KeyguardInteractor mKeyguardInteractor; 123 124 // Tracks the velocity of a touch to help filter out the touches that move too fast. 125 private VelocityTracker mVelocityTracker; 126 // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. 127 private int mActivePointerId = -1; 128 129 private boolean mIsDozing; 130 private boolean mIsActiveDreamLockscreenHosted; 131 private boolean mIsBouncerShowing; 132 private boolean mRunningFPS; 133 private boolean mCanDismissLockScreen; 134 private int mStatusBarState; 135 private boolean mIsKeyguardShowing; 136 private Runnable mOnGestureDetectedRunnable; 137 private Runnable mLongPressCancelRunnable; 138 139 private boolean mUdfpsSupported; 140 private float mHeightPixels; 141 private float mWidthPixels; 142 private int mBottomPaddingPx; 143 private int mDefaultPaddingPx; 144 145 private boolean mShowUnlockIcon; 146 private boolean mShowLockIcon; 147 148 // for udfps when strong auth is required or unlocked on AOD 149 private boolean mShowAodLockIcon; 150 private boolean mShowAodUnlockedIcon; 151 private final int mMaxBurnInOffsetX; 152 private final int mMaxBurnInOffsetY; 153 private float mInterpolatedDarkAmount; 154 155 private boolean mDownDetected; 156 private final Rect mSensorTouchLocation = new Rect(); 157 158 @VisibleForTesting 159 final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> { 160 mInterpolatedDarkAmount = step.getValue(); 161 mView.setDozeAmount(step.getValue()); 162 updateBurnInOffsets(); 163 }; 164 165 @VisibleForTesting 166 final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> { 167 mIsDozing = isDozing; 168 updateBurnInOffsets(); 169 updateVisibility(); 170 }; 171 172 @VisibleForTesting 173 final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = 174 (Boolean isLockscreenHosted) -> { 175 mIsActiveDreamLockscreenHosted = isLockscreenHosted; 176 updateVisibility(); 177 }; 178 179 @Inject LockIconViewController( @ullable LockIconView view, @NonNull StatusBarStateController statusBarStateController, @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, @NonNull KeyguardViewController keyguardViewController, @NonNull KeyguardStateController keyguardStateController, @NonNull FalsingManager falsingManager, @NonNull AuthController authController, @NonNull DumpManager dumpManager, @NonNull AccessibilityManager accessibilityManager, @NonNull ConfigurationController configurationController, @NonNull @Main DelayableExecutor executor, @NonNull VibratorHelper vibrator, @Nullable AuthRippleController authRippleController, @NonNull @Main Resources resources, @NonNull KeyguardTransitionInteractor transitionInteractor, @NonNull KeyguardInteractor keyguardInteractor, @NonNull FeatureFlags featureFlags, PrimaryBouncerInteractor primaryBouncerInteractor )180 public LockIconViewController( 181 @Nullable LockIconView view, 182 @NonNull StatusBarStateController statusBarStateController, 183 @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, 184 @NonNull KeyguardViewController keyguardViewController, 185 @NonNull KeyguardStateController keyguardStateController, 186 @NonNull FalsingManager falsingManager, 187 @NonNull AuthController authController, 188 @NonNull DumpManager dumpManager, 189 @NonNull AccessibilityManager accessibilityManager, 190 @NonNull ConfigurationController configurationController, 191 @NonNull @Main DelayableExecutor executor, 192 @NonNull VibratorHelper vibrator, 193 @Nullable AuthRippleController authRippleController, 194 @NonNull @Main Resources resources, 195 @NonNull KeyguardTransitionInteractor transitionInteractor, 196 @NonNull KeyguardInteractor keyguardInteractor, 197 @NonNull FeatureFlags featureFlags, 198 PrimaryBouncerInteractor primaryBouncerInteractor 199 ) { 200 super(view); 201 mStatusBarStateController = statusBarStateController; 202 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 203 mAuthController = authController; 204 mKeyguardViewController = keyguardViewController; 205 mKeyguardStateController = keyguardStateController; 206 mFalsingManager = falsingManager; 207 mAccessibilityManager = accessibilityManager; 208 mConfigurationController = configurationController; 209 mExecutor = executor; 210 mVibrator = vibrator; 211 mAuthRippleController = authRippleController; 212 mTransitionInteractor = transitionInteractor; 213 mKeyguardInteractor = keyguardInteractor; 214 mFeatureFlags = featureFlags; 215 mPrimaryBouncerInteractor = primaryBouncerInteractor; 216 217 mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); 218 mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); 219 220 mIcon = (AnimatedStateListDrawable) 221 resources.getDrawable(R.drawable.super_lock_icon, mView.getContext().getTheme()); 222 mView.setImageDrawable(mIcon); 223 mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button); 224 mLockedLabel = resources.getString(R.string.accessibility_lock_icon); 225 mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress); 226 dumpManager.registerDumpable(TAG, this); 227 } 228 229 @Override onInit()230 protected void onInit() { 231 mView.setAccessibilityDelegate(mAccessibilityDelegate); 232 233 if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { 234 collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(), 235 mDozeTransitionCallback); 236 collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); 237 } 238 239 if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { 240 collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(), 241 mIsActiveDreamLockscreenHostedCallback); 242 } 243 } 244 245 @Override onViewAttached()246 protected void onViewAttached() { 247 updateIsUdfpsEnrolled(); 248 updateConfiguration(); 249 updateKeyguardShowing(); 250 251 mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); 252 mIsDozing = mStatusBarStateController.isDozing(); 253 mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount(); 254 mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); 255 mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); 256 mStatusBarState = mStatusBarStateController.getState(); 257 258 updateColors(); 259 mConfigurationController.addCallback(mConfigurationListener); 260 261 mAuthController.addCallback(mAuthControllerCallback); 262 mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); 263 mStatusBarStateController.addCallback(mStatusBarStateListener); 264 mKeyguardStateController.addCallback(mKeyguardStateCallback); 265 mDownDetected = false; 266 updateBurnInOffsets(); 267 updateVisibility(); 268 269 mAccessibilityManager.addAccessibilityStateChangeListener( 270 mAccessibilityStateChangeListener); 271 updateAccessibility(); 272 } 273 updateAccessibility()274 private void updateAccessibility() { 275 if (mAccessibilityManager.isEnabled()) { 276 mView.setOnClickListener(mA11yClickListener); 277 } else { 278 mView.setOnClickListener(null); 279 } 280 } 281 282 @Override onViewDetached()283 protected void onViewDetached() { 284 mAuthController.removeCallback(mAuthControllerCallback); 285 mConfigurationController.removeCallback(mConfigurationListener); 286 mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); 287 mStatusBarStateController.removeCallback(mStatusBarStateListener); 288 mKeyguardStateController.removeCallback(mKeyguardStateCallback); 289 290 mAccessibilityManager.removeAccessibilityStateChangeListener( 291 mAccessibilityStateChangeListener); 292 } 293 getTop()294 public float getTop() { 295 return mView.getLocationTop(); 296 } 297 getBottom()298 public float getBottom() { 299 return mView.getLocationBottom(); 300 } 301 updateVisibility()302 private void updateVisibility() { 303 if (!mIsKeyguardShowing && !mIsDozing) { 304 mView.setVisibility(View.INVISIBLE); 305 return; 306 } 307 308 if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) { 309 mView.setVisibility(View.INVISIBLE); 310 return; 311 } 312 313 boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon 314 && !mShowAodUnlockedIcon && !mShowAodLockIcon; 315 mShowLockIcon = !mCanDismissLockScreen && isLockScreen() 316 && (!mUdfpsEnrolled || !mRunningFPS); 317 mShowUnlockIcon = mCanDismissLockScreen && isLockScreen(); 318 mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen; 319 mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen; 320 321 final CharSequence prevContentDescription = mView.getContentDescription(); 322 if (mShowLockIcon) { 323 if (wasShowingFpIcon) { 324 // fp icon was shown by UdfpsView, and now we still want to animate the transition 325 // in this drawable 326 mView.updateIcon(ICON_FINGERPRINT, false); 327 } 328 mView.updateIcon(ICON_LOCK, false); 329 mView.setContentDescription(mLockedLabel); 330 mView.setVisibility(View.VISIBLE); 331 } else if (mShowUnlockIcon) { 332 if (wasShowingFpIcon) { 333 // fp icon was shown by UdfpsView, and now we still want to animate the transition 334 // in this drawable 335 mView.updateIcon(ICON_FINGERPRINT, false); 336 } 337 mView.updateIcon(ICON_UNLOCK, false); 338 mView.setContentDescription(mUnlockedLabel); 339 mView.setVisibility(View.VISIBLE); 340 } else if (mShowAodUnlockedIcon) { 341 mView.updateIcon(ICON_UNLOCK, true); 342 mView.setContentDescription(mUnlockedLabel); 343 mView.setVisibility(View.VISIBLE); 344 } else if (mShowAodLockIcon) { 345 mView.updateIcon(ICON_LOCK, true); 346 mView.setContentDescription(mLockedLabel); 347 mView.setVisibility(View.VISIBLE); 348 } else { 349 mView.clearIcon(); 350 mView.setVisibility(View.INVISIBLE); 351 mView.setContentDescription(null); 352 } 353 354 boolean accessibilityEnabled = 355 !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser(); 356 mView.setImportantForAccessibility( 357 accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES 358 : View.IMPORTANT_FOR_ACCESSIBILITY_NO); 359 360 if (!Objects.equals(prevContentDescription, mView.getContentDescription()) 361 && mView.getContentDescription() != null && accessibilityEnabled) { 362 mView.announceForAccessibility(mView.getContentDescription()); 363 } 364 } 365 366 private final View.AccessibilityDelegate mAccessibilityDelegate = 367 new View.AccessibilityDelegate() { 368 private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint = 369 new AccessibilityNodeInfo.AccessibilityAction( 370 AccessibilityNodeInfoCompat.ACTION_CLICK, 371 getResources().getString(R.string.accessibility_authenticate_hint)); 372 private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint = 373 new AccessibilityNodeInfo.AccessibilityAction( 374 AccessibilityNodeInfoCompat.ACTION_CLICK, 375 getResources().getString(R.string.accessibility_enter_hint)); 376 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) { 377 super.onInitializeAccessibilityNodeInfo(v, info); 378 if (isActionable()) { 379 if (mShowLockIcon) { 380 info.addAction(mAccessibilityAuthenticateHint); 381 } else if (mShowUnlockIcon) { 382 info.addAction(mAccessibilityEnterHint); 383 } 384 } 385 } 386 }; 387 isLockScreen()388 private boolean isLockScreen() { 389 return !mIsDozing 390 && !mIsBouncerShowing 391 && mStatusBarState == StatusBarState.KEYGUARD; 392 } 393 updateKeyguardShowing()394 private void updateKeyguardShowing() { 395 mIsKeyguardShowing = mKeyguardStateController.isShowing() 396 && !mKeyguardStateController.isKeyguardGoingAway(); 397 } 398 updateColors()399 private void updateColors() { 400 mView.updateColorAndBackgroundVisibility(); 401 } 402 updateConfiguration()403 private void updateConfiguration() { 404 WindowManager windowManager = getContext().getSystemService(WindowManager.class); 405 Rect bounds = windowManager.getCurrentWindowMetrics().getBounds(); 406 mWidthPixels = bounds.right; 407 mHeightPixels = bounds.bottom; 408 mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom); 409 mDefaultPaddingPx = 410 getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); 411 412 mUnlockedLabel = mView.getContext().getResources().getString( 413 R.string.accessibility_unlock_button); 414 mLockedLabel = mView.getContext() 415 .getResources().getString(R.string.accessibility_lock_icon); 416 updateLockIconLocation(); 417 } 418 updateLockIconLocation()419 private void updateLockIconLocation() { 420 final float scaleFactor = mAuthController.getScaleFactor(); 421 final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); 422 if (mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) { 423 mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding, 424 scaledPadding); 425 } else { 426 if (mUdfpsSupported) { 427 mView.setCenterLocation(mAuthController.getUdfpsLocation(), 428 mAuthController.getUdfpsRadius(), scaledPadding); 429 } else { 430 mView.setCenterLocation( 431 new Point((int) mWidthPixels / 2, 432 (int) (mHeightPixels 433 - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))), 434 sLockIconRadiusPx * scaleFactor, scaledPadding); 435 } 436 } 437 } 438 439 @Override dump(@onNull PrintWriter pw, @NonNull String[] args)440 public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { 441 pw.println("mUdfpsSupported: " + mUdfpsSupported); 442 pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled); 443 pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing); 444 pw.println(" mIcon: "); 445 for (int state : mIcon.getState()) { 446 pw.print(" " + state); 447 } 448 pw.println(); 449 pw.println(" mShowUnlockIcon: " + mShowUnlockIcon); 450 pw.println(" mShowLockIcon: " + mShowLockIcon); 451 pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon); 452 pw.println(); 453 pw.println(" mIsDozing: " + mIsDozing); 454 pw.println(" isFlagEnabled(DOZING_MIGRATION_1): " 455 + mFeatureFlags.isEnabled(DOZING_MIGRATION_1)); 456 pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); 457 pw.println(" mRunningFPS: " + mRunningFPS); 458 pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); 459 pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); 460 pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); 461 pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); 462 pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx); 463 pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted); 464 465 if (mView != null) { 466 mView.dump(pw, args); 467 } 468 } 469 470 /** Every minute, update the aod icon's burn in offset */ dozeTimeTick()471 public void dozeTimeTick() { 472 updateBurnInOffsets(); 473 } 474 updateBurnInOffsets()475 private void updateBurnInOffsets() { 476 float offsetX = MathUtils.lerp(0f, 477 getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) 478 - mMaxBurnInOffsetX, mInterpolatedDarkAmount); 479 float offsetY = MathUtils.lerp(0f, 480 getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) 481 - mMaxBurnInOffsetY, mInterpolatedDarkAmount); 482 483 mView.setTranslationX(offsetX); 484 mView.setTranslationY(offsetY); 485 } 486 updateIsUdfpsEnrolled()487 private void updateIsUdfpsEnrolled() { 488 boolean wasUdfpsSupported = mUdfpsSupported; 489 boolean wasUdfpsEnrolled = mUdfpsEnrolled; 490 491 mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported(); 492 mView.setUseBackground(mUdfpsSupported); 493 494 mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled(); 495 if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) { 496 updateVisibility(); 497 } 498 } 499 500 private StatusBarStateController.StateListener mStatusBarStateListener = 501 new StatusBarStateController.StateListener() { 502 @Override 503 public void onDozeAmountChanged(float linear, float eased) { 504 if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { 505 mInterpolatedDarkAmount = eased; 506 mView.setDozeAmount(eased); 507 updateBurnInOffsets(); 508 } 509 } 510 511 @Override 512 public void onDozingChanged(boolean isDozing) { 513 if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { 514 mIsDozing = isDozing; 515 updateBurnInOffsets(); 516 updateVisibility(); 517 } 518 } 519 520 @Override 521 public void onStateChanged(int statusBarState) { 522 mStatusBarState = statusBarState; 523 updateVisibility(); 524 } 525 }; 526 527 private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = 528 new KeyguardUpdateMonitorCallback() { 529 @Override 530 public void onKeyguardBouncerStateChanged(boolean bouncer) { 531 mIsBouncerShowing = bouncer; 532 updateVisibility(); 533 } 534 535 @Override 536 public void onBiometricRunningStateChanged(boolean running, 537 BiometricSourceType biometricSourceType) { 538 final boolean wasRunningFps = mRunningFPS; 539 540 if (biometricSourceType == FINGERPRINT) { 541 mRunningFPS = running; 542 } 543 544 if (wasRunningFps != mRunningFPS) { 545 updateVisibility(); 546 } 547 } 548 }; 549 550 private final KeyguardStateController.Callback mKeyguardStateCallback = 551 new KeyguardStateController.Callback() { 552 @Override 553 public void onUnlockedChanged() { 554 mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); 555 updateKeyguardShowing(); 556 updateVisibility(); 557 } 558 559 @Override 560 public void onKeyguardShowingChanged() { 561 // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe). 562 // If biometrics were removed, local vars mCanDismissLockScreen and 563 // mUserUnlockedWithBiometric may not be updated. 564 mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); 565 566 // reset mIsBouncerShowing state in case it was preemptively set 567 // onLongPress 568 mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); 569 570 updateKeyguardShowing(); 571 updateVisibility(); 572 } 573 574 @Override 575 public void onKeyguardFadingAwayChanged() { 576 updateKeyguardShowing(); 577 updateVisibility(); 578 } 579 }; 580 581 private final ConfigurationController.ConfigurationListener mConfigurationListener = 582 new ConfigurationController.ConfigurationListener() { 583 @Override 584 public void onUiModeChanged() { 585 updateColors(); 586 } 587 588 @Override 589 public void onThemeChanged() { 590 updateColors(); 591 } 592 593 @Override 594 public void onConfigChanged(Configuration newConfig) { 595 updateConfiguration(); 596 updateColors(); 597 } 598 }; 599 600 /** 601 * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true. 602 * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon 603 * area for {@link #mLongPressTimeout} ms. 604 * 605 * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. 606 */ onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable)607 public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) { 608 if (!onInterceptTouchEvent(event)) { 609 cancelTouches(); 610 return false; 611 } 612 613 mOnGestureDetectedRunnable = onGestureDetectedRunnable; 614 switch(event.getActionMasked()) { 615 case MotionEvent.ACTION_DOWN: 616 case MotionEvent.ACTION_HOVER_ENTER: 617 if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) { 618 vibrateOnTouchExploration(); 619 } 620 621 // The pointer that causes ACTION_DOWN is always at index 0. 622 // We need to persist its ID to track it during ACTION_MOVE that could include 623 // data for many other pointers because of multi-touch support. 624 mActivePointerId = event.getPointerId(0); 625 if (mVelocityTracker == null) { 626 // To simplify the lifecycle of the velocity tracker, make sure it's never null 627 // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. 628 mVelocityTracker = VelocityTracker.obtain(); 629 } else { 630 // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new 631 // ACTION_DOWN, in that case we should just reuse the old instance. 632 mVelocityTracker.clear(); 633 } 634 mVelocityTracker.addMovement(event); 635 636 mDownDetected = true; 637 mLongPressCancelRunnable = mExecutor.executeDelayed( 638 this::onLongPress, mLongPressTimeout); 639 break; 640 case MotionEvent.ACTION_MOVE: 641 case MotionEvent.ACTION_HOVER_MOVE: 642 mVelocityTracker.addMovement(event); 643 // Compute pointer velocity in pixels per second. 644 mVelocityTracker.computeCurrentVelocity(1000); 645 float velocity = UdfpsController.computePointerSpeed(mVelocityTracker, 646 mActivePointerId); 647 if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS 648 && UdfpsController.exceedsVelocityThreshold(velocity)) { 649 Log.v(TAG, "lock icon long-press rescheduled due to " 650 + "high pointer velocity=" + velocity); 651 mLongPressCancelRunnable.run(); 652 mLongPressCancelRunnable = mExecutor.executeDelayed( 653 this::onLongPress, mLongPressTimeout); 654 } 655 break; 656 case MotionEvent.ACTION_UP: 657 case MotionEvent.ACTION_CANCEL: 658 case MotionEvent.ACTION_HOVER_EXIT: 659 cancelTouches(); 660 break; 661 } 662 663 return true; 664 } 665 666 /** 667 * Intercepts the touch if the onDown event and current event are within this lock icon view's 668 * bounds. 669 */ onInterceptTouchEvent(MotionEvent event)670 public boolean onInterceptTouchEvent(MotionEvent event) { 671 if (!inLockIconArea(event) || !isActionable()) { 672 return false; 673 } 674 675 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 676 return true; 677 } 678 679 return mDownDetected; 680 } 681 onLongPress()682 private void onLongPress() { 683 cancelTouches(); 684 if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { 685 Log.v(TAG, "lock icon long-press rejected by the falsing manager."); 686 return; 687 } 688 689 // pre-emptively set to true to hide view 690 mIsBouncerShowing = true; 691 if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) { 692 mAuthRippleController.showUnlockRipple(FINGERPRINT); 693 } 694 updateVisibility(); 695 if (mOnGestureDetectedRunnable != null) { 696 mOnGestureDetectedRunnable.run(); 697 } 698 699 // play device entry haptic (consistent with UDFPS controller longpress) 700 vibrateOnLongPress(); 701 702 mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); 703 } 704 705 cancelTouches()706 private void cancelTouches() { 707 mDownDetected = false; 708 if (mLongPressCancelRunnable != null) { 709 mLongPressCancelRunnable.run(); 710 } 711 if (mVelocityTracker != null) { 712 mVelocityTracker.recycle(); 713 mVelocityTracker = null; 714 } 715 } 716 inLockIconArea(MotionEvent event)717 private boolean inLockIconArea(MotionEvent event) { 718 mView.getHitRect(mSensorTouchLocation); 719 return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) 720 && mView.getVisibility() == View.VISIBLE; 721 } 722 isActionable()723 private boolean isActionable() { 724 if (mIsBouncerShowing) { 725 Log.v(TAG, "lock icon long-press ignored, bouncer already showing."); 726 // a long press gestures from AOD may have already triggered the bouncer to show, 727 // so this touch is no longer actionable 728 return false; 729 } 730 return mUdfpsSupported || mShowUnlockIcon; 731 } 732 733 /** 734 * Set the alpha of this view. 735 */ setAlpha(float alpha)736 public void setAlpha(float alpha) { 737 mView.setAlpha(alpha); 738 } 739 updateUdfpsConfig()740 private void updateUdfpsConfig() { 741 // must be called from the main thread since it may update the views 742 mExecutor.execute(() -> { 743 updateIsUdfpsEnrolled(); 744 updateConfiguration(); 745 }); 746 } 747 748 @VisibleForTesting vibrateOnTouchExploration()749 void vibrateOnTouchExploration() { 750 if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { 751 mVibrator.performHapticFeedback( 752 mView, 753 HapticFeedbackConstants.CONTEXT_CLICK 754 ); 755 } else { 756 mVibrator.vibrate( 757 Process.myUid(), 758 getContext().getOpPackageName(), 759 UdfpsController.EFFECT_CLICK, 760 "lock-icon-down", 761 TOUCH_VIBRATION_ATTRIBUTES); 762 } 763 } 764 765 @VisibleForTesting vibrateOnLongPress()766 void vibrateOnLongPress() { 767 if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { 768 mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS); 769 } else { 770 mVibrator.vibrate( 771 Process.myUid(), 772 getContext().getOpPackageName(), 773 UdfpsController.EFFECT_CLICK, 774 "lock-screen-lock-icon-longpress", 775 TOUCH_VIBRATION_ATTRIBUTES); 776 } 777 } 778 779 private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { 780 @Override 781 public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { 782 if (modality == TYPE_FINGERPRINT) { 783 updateUdfpsConfig(); 784 } 785 } 786 787 @Override 788 public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { 789 if (modality == TYPE_FINGERPRINT) { 790 updateUdfpsConfig(); 791 } 792 } 793 794 @Override 795 public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) { 796 updateUdfpsConfig(); 797 } 798 }; 799 800 private final View.OnClickListener mA11yClickListener = v -> onLongPress(); 801 802 private final AccessibilityManager.AccessibilityStateChangeListener 803 mAccessibilityStateChangeListener = enabled -> updateAccessibility(); 804 } 805