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