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