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