1 /* 2 * Copyright (C) 2014 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.statusbar.phone; 18 19 import android.content.ComponentCallbacks2; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.os.SystemClock; 23 import android.os.Trace; 24 import android.view.KeyEvent; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.view.ViewRootImpl; 28 import android.view.WindowManagerGlobal; 29 30 import com.android.internal.widget.LockPatternUtils; 31 import com.android.keyguard.KeyguardUpdateMonitor; 32 import com.android.keyguard.ViewMediatorCallback; 33 import com.android.systemui.SystemUIFactory; 34 import com.android.systemui.statusbar.CommandQueue; 35 import com.android.systemui.statusbar.RemoteInputController; 36 37 import static com.android.keyguard.KeyguardHostView.OnDismissAction; 38 39 /** 40 * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back 41 * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, 42 * which is in turn, reported to this class by the current 43 * {@link com.android.keyguard.KeyguardViewBase}. 44 */ 45 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback { 46 47 // When hiding the Keyguard with timing supplied from WindowManager, better be early than late. 48 private static final long HIDE_TIMING_CORRECTION_MS = -3 * 16; 49 50 // Delay for showing the navigation bar when the bouncer appears. This should be kept in sync 51 // with the appear animations of the PIN/pattern/password views. 52 private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320; 53 54 private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200; 55 56 private static String TAG = "StatusBarKeyguardViewManager"; 57 58 protected final Context mContext; 59 60 protected LockPatternUtils mLockPatternUtils; 61 protected ViewMediatorCallback mViewMediatorCallback; 62 protected PhoneStatusBar mPhoneStatusBar; 63 private ScrimController mScrimController; 64 private FingerprintUnlockController mFingerprintUnlockController; 65 66 private ViewGroup mContainer; 67 private StatusBarWindowManager mStatusBarWindowManager; 68 69 private boolean mDeviceInteractive = false; 70 private boolean mScreenTurnedOn; 71 protected KeyguardBouncer mBouncer; 72 protected boolean mShowing; 73 protected boolean mOccluded; 74 protected boolean mRemoteInputActive; 75 76 protected boolean mFirstUpdate = true; 77 protected boolean mLastShowing; 78 protected boolean mLastOccluded; 79 private boolean mLastBouncerShowing; 80 private boolean mLastBouncerDismissible; 81 protected boolean mLastRemoteInputActive; 82 83 private OnDismissAction mAfterKeyguardGoneAction; 84 private boolean mDeviceWillWakeUp; 85 private boolean mDeferScrimFadeOut; 86 StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils)87 public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback, 88 LockPatternUtils lockPatternUtils) { 89 mContext = context; 90 mViewMediatorCallback = callback; 91 mLockPatternUtils = lockPatternUtils; 92 } 93 registerStatusBar(PhoneStatusBar phoneStatusBar, ViewGroup container, StatusBarWindowManager statusBarWindowManager, ScrimController scrimController, FingerprintUnlockController fingerprintUnlockController)94 public void registerStatusBar(PhoneStatusBar phoneStatusBar, 95 ViewGroup container, StatusBarWindowManager statusBarWindowManager, 96 ScrimController scrimController, 97 FingerprintUnlockController fingerprintUnlockController) { 98 mPhoneStatusBar = phoneStatusBar; 99 mContainer = container; 100 mStatusBarWindowManager = statusBarWindowManager; 101 mScrimController = scrimController; 102 mFingerprintUnlockController = fingerprintUnlockController; 103 mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, 104 mViewMediatorCallback, mLockPatternUtils, mStatusBarWindowManager, container); 105 } 106 107 /** 108 * Show the keyguard. Will handle creating and attaching to the view manager 109 * lazily. 110 */ show(Bundle options)111 public void show(Bundle options) { 112 mShowing = true; 113 mStatusBarWindowManager.setKeyguardShowing(true); 114 mScrimController.abortKeyguardFadingOut(); 115 reset(); 116 } 117 118 /** 119 * Shows the notification keyguard or the bouncer depending on 120 * {@link KeyguardBouncer#needsFullscreenBouncer()}. 121 */ showBouncerOrKeyguard()122 protected void showBouncerOrKeyguard() { 123 if (mBouncer.needsFullscreenBouncer()) { 124 125 // The keyguard might be showing (already). So we need to hide it. 126 mPhoneStatusBar.hideKeyguard(); 127 mBouncer.show(true /* resetSecuritySelection */); 128 } else { 129 mPhoneStatusBar.showKeyguard(); 130 mBouncer.hide(false /* destroyView */); 131 mBouncer.prepare(); 132 } 133 } 134 showBouncer()135 private void showBouncer() { 136 if (mShowing) { 137 mBouncer.show(false /* resetSecuritySelection */); 138 } 139 updateStates(); 140 } 141 dismissWithAction(OnDismissAction r, Runnable cancelAction, boolean afterKeyguardGone)142 public void dismissWithAction(OnDismissAction r, Runnable cancelAction, 143 boolean afterKeyguardGone) { 144 if (mShowing) { 145 if (!afterKeyguardGone) { 146 mBouncer.showWithDismissAction(r, cancelAction); 147 } else { 148 mBouncer.show(false /* resetSecuritySelection */); 149 mAfterKeyguardGoneAction = r; 150 } 151 } 152 updateStates(); 153 } 154 155 /** 156 * Reset the state of the view. 157 */ reset()158 public void reset() { 159 if (mShowing) { 160 if (mOccluded) { 161 mPhoneStatusBar.hideKeyguard(); 162 mPhoneStatusBar.stopWaitingForKeyguardExit(); 163 mBouncer.hide(false /* destroyView */); 164 } else { 165 showBouncerOrKeyguard(); 166 } 167 KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset(); 168 updateStates(); 169 } 170 } 171 onStartedGoingToSleep()172 public void onStartedGoingToSleep() { 173 mPhoneStatusBar.onStartedGoingToSleep(); 174 } 175 onFinishedGoingToSleep()176 public void onFinishedGoingToSleep() { 177 mDeviceInteractive = false; 178 mPhoneStatusBar.onFinishedGoingToSleep(); 179 mBouncer.onScreenTurnedOff(); 180 } 181 onStartedWakingUp()182 public void onStartedWakingUp() { 183 mDeviceInteractive = true; 184 mDeviceWillWakeUp = false; 185 mPhoneStatusBar.onStartedWakingUp(); 186 } 187 onScreenTurningOn()188 public void onScreenTurningOn() { 189 mPhoneStatusBar.onScreenTurningOn(); 190 } 191 isScreenTurnedOn()192 public boolean isScreenTurnedOn() { 193 return mScreenTurnedOn; 194 } 195 onScreenTurnedOn()196 public void onScreenTurnedOn() { 197 mScreenTurnedOn = true; 198 if (mDeferScrimFadeOut) { 199 mDeferScrimFadeOut = false; 200 animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS, 201 true /* skipFirstFrame */); 202 updateStates(); 203 } 204 mPhoneStatusBar.onScreenTurnedOn(); 205 } 206 207 @Override onRemoteInputActive(boolean active)208 public void onRemoteInputActive(boolean active) { 209 mRemoteInputActive = active; 210 updateStates(); 211 } 212 onScreenTurnedOff()213 public void onScreenTurnedOff() { 214 mScreenTurnedOn = false; 215 mPhoneStatusBar.onScreenTurnedOff(); 216 } 217 notifyDeviceWakeUpRequested()218 public void notifyDeviceWakeUpRequested() { 219 mDeviceWillWakeUp = !mDeviceInteractive; 220 } 221 verifyUnlock()222 public void verifyUnlock() { 223 dismiss(); 224 } 225 setNeedsInput(boolean needsInput)226 public void setNeedsInput(boolean needsInput) { 227 mStatusBarWindowManager.setKeyguardNeedsInput(needsInput); 228 } 229 isUnlockWithWallpaper()230 public boolean isUnlockWithWallpaper() { 231 return mStatusBarWindowManager.isShowingWallpaper(); 232 } 233 setOccluded(boolean occluded)234 public void setOccluded(boolean occluded) { 235 if (occluded && !mOccluded && mShowing) { 236 if (mPhoneStatusBar.isInLaunchTransition()) { 237 mOccluded = true; 238 mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */, 239 new Runnable() { 240 @Override 241 public void run() { 242 mStatusBarWindowManager.setKeyguardOccluded(mOccluded); 243 reset(); 244 } 245 }); 246 return; 247 } 248 } 249 mOccluded = occluded; 250 mPhoneStatusBar.updateMediaMetaData(false, false); 251 mStatusBarWindowManager.setKeyguardOccluded(occluded); 252 reset(); 253 } 254 isOccluded()255 public boolean isOccluded() { 256 return mOccluded; 257 } 258 259 /** 260 * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the 261 * security view of the bouncer. 262 * 263 * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if 264 * no action should be run 265 */ startPreHideAnimation(Runnable finishRunnable)266 public void startPreHideAnimation(Runnable finishRunnable) { 267 if (mBouncer.isShowing()) { 268 mBouncer.startPreHideAnimation(finishRunnable); 269 } else if (finishRunnable != null) { 270 finishRunnable.run(); 271 } 272 } 273 274 /** 275 * Hides the keyguard view 276 */ hide(long startTime, final long fadeoutDuration)277 public void hide(long startTime, final long fadeoutDuration) { 278 mShowing = false; 279 280 long uptimeMillis = SystemClock.uptimeMillis(); 281 long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis); 282 283 if (mPhoneStatusBar.isInLaunchTransition() ) { 284 mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() { 285 @Override 286 public void run() { 287 mStatusBarWindowManager.setKeyguardShowing(false); 288 mStatusBarWindowManager.setKeyguardFadingAway(true); 289 mBouncer.hide(true /* destroyView */); 290 updateStates(); 291 mScrimController.animateKeyguardFadingOut( 292 PhoneStatusBar.FADE_KEYGUARD_START_DELAY, 293 PhoneStatusBar.FADE_KEYGUARD_DURATION, null, 294 false /* skipFirstFrame */); 295 } 296 }, new Runnable() { 297 @Override 298 public void run() { 299 mPhoneStatusBar.hideKeyguard(); 300 mStatusBarWindowManager.setKeyguardFadingAway(false); 301 mViewMediatorCallback.keyguardGone(); 302 executeAfterKeyguardGoneAction(); 303 } 304 }); 305 } else { 306 if (mFingerprintUnlockController.getMode() 307 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) { 308 mFingerprintUnlockController.startKeyguardFadingAway(); 309 mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240); 310 mStatusBarWindowManager.setKeyguardFadingAway(true); 311 mPhoneStatusBar.fadeKeyguardWhilePulsing(); 312 animateScrimControllerKeyguardFadingOut(0, 240, new Runnable() { 313 @Override 314 public void run() { 315 mPhoneStatusBar.hideKeyguard(); 316 } 317 }, false /* skipFirstFrame */); 318 } else { 319 mFingerprintUnlockController.startKeyguardFadingAway(); 320 mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration); 321 boolean staying = mPhoneStatusBar.hideKeyguard(); 322 if (!staying) { 323 mStatusBarWindowManager.setKeyguardFadingAway(true); 324 if (mFingerprintUnlockController.getMode() 325 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) { 326 if (!mScreenTurnedOn) { 327 mDeferScrimFadeOut = true; 328 } else { 329 330 // Screen is already on, don't defer with fading out. 331 animateScrimControllerKeyguardFadingOut(0, 332 WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS, 333 true /* skipFirstFrame */); 334 } 335 } else { 336 animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration, 337 false /* skipFirstFrame */); 338 } 339 } else { 340 mScrimController.animateGoingToFullShade(delay, fadeoutDuration); 341 mPhoneStatusBar.finishKeyguardFadingAway(); 342 } 343 } 344 mStatusBarWindowManager.setKeyguardShowing(false); 345 mBouncer.hide(true /* destroyView */); 346 mViewMediatorCallback.keyguardGone(); 347 executeAfterKeyguardGoneAction(); 348 updateStates(); 349 } 350 } 351 onDensityOrFontScaleChanged()352 public void onDensityOrFontScaleChanged() { 353 mBouncer.hide(true /* destroyView */); 354 } 355 animateScrimControllerKeyguardFadingOut(long delay, long duration, boolean skipFirstFrame)356 private void animateScrimControllerKeyguardFadingOut(long delay, long duration, 357 boolean skipFirstFrame) { 358 animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */, 359 skipFirstFrame); 360 } 361 animateScrimControllerKeyguardFadingOut(long delay, long duration, final Runnable endRunnable, boolean skipFirstFrame)362 private void animateScrimControllerKeyguardFadingOut(long delay, long duration, 363 final Runnable endRunnable, boolean skipFirstFrame) { 364 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0); 365 mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() { 366 @Override 367 public void run() { 368 if (endRunnable != null) { 369 endRunnable.run(); 370 } 371 mStatusBarWindowManager.setKeyguardFadingAway(false); 372 mPhoneStatusBar.finishKeyguardFadingAway(); 373 mFingerprintUnlockController.finishKeyguardFadingAway(); 374 WindowManagerGlobal.getInstance().trimMemory( 375 ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); 376 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0); 377 } 378 }, skipFirstFrame); 379 } 380 executeAfterKeyguardGoneAction()381 private void executeAfterKeyguardGoneAction() { 382 if (mAfterKeyguardGoneAction != null) { 383 mAfterKeyguardGoneAction.onDismiss(); 384 mAfterKeyguardGoneAction = null; 385 } 386 } 387 388 /** 389 * Dismisses the keyguard by going to the next screen or making it gone. 390 */ dismiss()391 public void dismiss() { 392 if (mDeviceInteractive || mDeviceWillWakeUp) { 393 showBouncer(); 394 } 395 } 396 397 /** 398 * WARNING: This method might cause Binder calls. 399 */ isSecure()400 public boolean isSecure() { 401 return mBouncer.isSecure(); 402 } 403 404 /** 405 * @return Whether the keyguard is showing 406 */ isShowing()407 public boolean isShowing() { 408 return mShowing; 409 } 410 411 /** 412 * Notifies this manager that the back button has been pressed. 413 * 414 * @return whether the back press has been handled 415 */ onBackPressed()416 public boolean onBackPressed() { 417 if (mBouncer.isShowing()) { 418 mPhoneStatusBar.endAffordanceLaunch(); 419 reset(); 420 return true; 421 } 422 return false; 423 } 424 isBouncerShowing()425 public boolean isBouncerShowing() { 426 return mBouncer.isShowing(); 427 } 428 getNavBarShowDelay()429 private long getNavBarShowDelay() { 430 if (mPhoneStatusBar.isKeyguardFadingAway()) { 431 return mPhoneStatusBar.getKeyguardFadingAwayDelay(); 432 } else { 433 434 // Keyguard is not going away, thus we are showing the navigation bar because the 435 // bouncer is appearing. 436 return NAV_BAR_SHOW_DELAY_BOUNCER; 437 } 438 } 439 440 private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() { 441 @Override 442 public void run() { 443 mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE); 444 } 445 }; 446 updateStates()447 protected void updateStates() { 448 int vis = mContainer.getSystemUiVisibility(); 449 boolean showing = mShowing; 450 boolean occluded = mOccluded; 451 boolean bouncerShowing = mBouncer.isShowing(); 452 boolean bouncerDismissible = !mBouncer.isFullscreenBouncer(); 453 boolean remoteInputActive = mRemoteInputActive; 454 455 if ((bouncerDismissible || !showing || remoteInputActive) != 456 (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive) 457 || mFirstUpdate) { 458 if (bouncerDismissible || !showing || remoteInputActive) { 459 mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK); 460 } else { 461 mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK); 462 } 463 } 464 465 boolean navBarVisible = isNavBarVisible(); 466 boolean lastNavBarVisible = getLastNavBarVisible(); 467 if (navBarVisible != lastNavBarVisible || mFirstUpdate) { 468 if (mPhoneStatusBar.getNavigationBarView() != null) { 469 if (navBarVisible) { 470 long delay = getNavBarShowDelay(); 471 if (delay == 0) { 472 mMakeNavigationBarVisibleRunnable.run(); 473 } else { 474 mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable, 475 delay); 476 } 477 } else { 478 mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable); 479 mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE); 480 } 481 } 482 } 483 484 if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { 485 mStatusBarWindowManager.setBouncerShowing(bouncerShowing); 486 mPhoneStatusBar.setBouncerShowing(bouncerShowing); 487 mScrimController.setBouncerShowing(bouncerShowing); 488 } 489 490 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 491 if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) { 492 updateMonitor.onKeyguardVisibilityChanged(showing && !occluded); 493 } 494 if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { 495 updateMonitor.sendKeyguardBouncerChanged(bouncerShowing); 496 } 497 498 mFirstUpdate = false; 499 mLastShowing = showing; 500 mLastOccluded = occluded; 501 mLastBouncerShowing = bouncerShowing; 502 mLastBouncerDismissible = bouncerDismissible; 503 mLastRemoteInputActive = remoteInputActive; 504 505 mPhoneStatusBar.onKeyguardViewManagerStatesUpdated(); 506 } 507 508 /** 509 * @return Whether the navigation bar should be made visible based on the current state. 510 */ isNavBarVisible()511 protected boolean isNavBarVisible() { 512 return !(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive; 513 } 514 515 /** 516 * @return Whether the navigation bar was made visible based on the last known state. 517 */ getLastNavBarVisible()518 protected boolean getLastNavBarVisible() { 519 return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive; 520 } 521 shouldDismissOnMenuPressed()522 public boolean shouldDismissOnMenuPressed() { 523 return mBouncer.shouldDismissOnMenuPressed(); 524 } 525 interceptMediaKey(KeyEvent event)526 public boolean interceptMediaKey(KeyEvent event) { 527 return mBouncer.interceptMediaKey(event); 528 } 529 onActivityDrawn()530 public void onActivityDrawn() { 531 if (mPhoneStatusBar.isCollapsing()) { 532 mPhoneStatusBar.addPostCollapseAction(new Runnable() { 533 @Override 534 public void run() { 535 mViewMediatorCallback.readyForKeyguardDone(); 536 } 537 }); 538 } else { 539 mViewMediatorCallback.readyForKeyguardDone(); 540 } 541 } 542 shouldDisableWindowAnimationsForUnlock()543 public boolean shouldDisableWindowAnimationsForUnlock() { 544 return mPhoneStatusBar.isInLaunchTransition(); 545 } 546 isGoingToNotificationShade()547 public boolean isGoingToNotificationShade() { 548 return mPhoneStatusBar.isGoingToNotificationShade(); 549 } 550 isSecure(int userId)551 public boolean isSecure(int userId) { 552 return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId); 553 } 554 isInputRestricted()555 public boolean isInputRestricted() { 556 return mViewMediatorCallback.isInputRestricted(); 557 } 558 keyguardGoingAway()559 public void keyguardGoingAway() { 560 mPhoneStatusBar.keyguardGoingAway(); 561 } 562 animateCollapsePanels(float speedUpFactor)563 public void animateCollapsePanels(float speedUpFactor) { 564 mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */, 565 false /* delayed */, speedUpFactor); 566 } 567 568 /** 569 * Notifies that the user has authenticated by other means than using the bouncer, for example, 570 * fingerprint. 571 */ notifyKeyguardAuthenticated(boolean strongAuth)572 public void notifyKeyguardAuthenticated(boolean strongAuth) { 573 mBouncer.notifyKeyguardAuthenticated(strongAuth); 574 } 575 showBouncerMessage(String message, int color)576 public void showBouncerMessage(String message, int color) { 577 mBouncer.showMessage(message, color); 578 } 579 getViewRootImpl()580 public ViewRootImpl getViewRootImpl() { 581 return mPhoneStatusBar.getStatusBarView().getViewRootImpl(); 582 } 583 } 584