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.RemoteException; 23 import android.os.SystemClock; 24 import android.util.Slog; 25 import android.view.KeyEvent; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.WindowManagerGlobal; 29 30 import com.android.internal.policy.IKeyguardShowCallback; 31 import com.android.internal.widget.LockPatternUtils; 32 import com.android.keyguard.KeyguardUpdateMonitor; 33 import com.android.keyguard.ViewMediatorCallback; 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 String TAG = "StatusBarKeyguardViewManager"; 53 54 private final Context mContext; 55 56 private LockPatternUtils mLockPatternUtils; 57 private ViewMediatorCallback mViewMediatorCallback; 58 private PhoneStatusBar mPhoneStatusBar; 59 private ScrimController mScrimController; 60 61 private ViewGroup mContainer; 62 private StatusBarWindowManager mStatusBarWindowManager; 63 64 private boolean mScreenOn = false; 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 OnDismissAction mAfterKeyguardGoneAction; 75 76 public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback, 77 LockPatternUtils lockPatternUtils) { 78 mContext = context; 79 mViewMediatorCallback = callback; 80 mLockPatternUtils = lockPatternUtils; 81 } 82 83 public void registerStatusBar(PhoneStatusBar phoneStatusBar, 84 ViewGroup container, StatusBarWindowManager statusBarWindowManager, 85 ScrimController scrimController) { 86 mPhoneStatusBar = phoneStatusBar; 87 mContainer = container; 88 mStatusBarWindowManager = statusBarWindowManager; 89 mScrimController = scrimController; 90 mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, 91 mStatusBarWindowManager, container); 92 } 93 94 /** 95 * Show the keyguard. Will handle creating and attaching to the view manager 96 * lazily. 97 */ 98 public void show(Bundle options) { 99 mShowing = true; 100 mStatusBarWindowManager.setKeyguardShowing(true); 101 reset(); 102 } 103 104 /** 105 * Shows the notification keyguard or the bouncer depending on 106 * {@link KeyguardBouncer#needsFullscreenBouncer()}. 107 */ 108 private void showBouncerOrKeyguard() { 109 if (mBouncer.needsFullscreenBouncer()) { 110 111 // The keyguard might be showing (already). So we need to hide it. 112 mPhoneStatusBar.hideKeyguard(); 113 mBouncer.show(true /* resetSecuritySelection */); 114 } else { 115 mPhoneStatusBar.showKeyguard(); 116 mBouncer.hide(false /* destroyView */); 117 mBouncer.prepare(); 118 } 119 } 120 121 private void showBouncer() { 122 if (mShowing) { 123 mBouncer.show(false /* resetSecuritySelection */); 124 } 125 updateStates(); 126 } 127 128 public void dismissWithAction(OnDismissAction r, boolean afterKeyguardGone) { 129 if (mShowing) { 130 if (!afterKeyguardGone) { 131 mBouncer.showWithDismissAction(r); 132 } else { 133 mBouncer.show(false /* resetSecuritySelection */); 134 mAfterKeyguardGoneAction = r; 135 } 136 } 137 updateStates(); 138 } 139 140 /** 141 * Reset the state of the view. 142 */ 143 public void reset() { 144 if (mShowing) { 145 if (mOccluded) { 146 mPhoneStatusBar.hideKeyguard(); 147 mBouncer.hide(false /* destroyView */); 148 } else { 149 showBouncerOrKeyguard(); 150 } 151 updateStates(); 152 } 153 } 154 155 public void onScreenTurnedOff() { 156 mScreenOn = false; 157 mPhoneStatusBar.onScreenTurnedOff(); 158 mBouncer.onScreenTurnedOff(); 159 } 160 161 public void onScreenTurnedOn(final IKeyguardShowCallback callback) { 162 mScreenOn = true; 163 mPhoneStatusBar.onScreenTurnedOn(); 164 if (callback != null) { 165 callbackAfterDraw(callback); 166 } 167 } 168 169 private void callbackAfterDraw(final IKeyguardShowCallback callback) { 170 mContainer.post(new Runnable() { 171 @Override 172 public void run() { 173 try { 174 callback.onShown(mContainer.getWindowToken()); 175 } catch (RemoteException e) { 176 Slog.w(TAG, "Exception calling onShown():", e); 177 } 178 } 179 }); 180 } 181 182 public void verifyUnlock() { 183 dismiss(); 184 } 185 186 public void setNeedsInput(boolean needsInput) { 187 mStatusBarWindowManager.setKeyguardNeedsInput(needsInput); 188 } 189 190 public void updateUserActivityTimeout() { 191 mStatusBarWindowManager.setKeyguardUserActivityTimeout(mBouncer.getUserActivityTimeout()); 192 } 193 194 public void setOccluded(boolean occluded) { 195 if (occluded && !mOccluded && mShowing) { 196 if (mPhoneStatusBar.isInLaunchTransition()) { 197 mOccluded = true; 198 mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */, 199 new Runnable() { 200 @Override 201 public void run() { 202 mStatusBarWindowManager.setKeyguardOccluded(mOccluded); 203 reset(); 204 } 205 }); 206 return; 207 } 208 } 209 mOccluded = occluded; 210 mStatusBarWindowManager.setKeyguardOccluded(occluded); 211 reset(); 212 } 213 214 public boolean isOccluded() { 215 return mOccluded; 216 } 217 218 /** 219 * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the 220 * security view of the bouncer. 221 * 222 * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if 223 * no action should be run 224 */ 225 public void startPreHideAnimation(Runnable finishRunnable) { 226 if (mBouncer.isShowing()) { 227 mBouncer.startPreHideAnimation(finishRunnable); 228 } else if (finishRunnable != null) { 229 finishRunnable.run(); 230 } 231 } 232 233 /** 234 * Hides the keyguard view 235 */ 236 public void hide(long startTime, final long fadeoutDuration) { 237 mShowing = false; 238 239 long uptimeMillis = SystemClock.uptimeMillis(); 240 long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis); 241 242 if (mPhoneStatusBar.isInLaunchTransition() ) { 243 mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() { 244 @Override 245 public void run() { 246 mStatusBarWindowManager.setKeyguardShowing(false); 247 mStatusBarWindowManager.setKeyguardFadingAway(true); 248 mBouncer.hide(true /* destroyView */); 249 updateStates(); 250 mScrimController.animateKeyguardFadingOut( 251 PhoneStatusBar.FADE_KEYGUARD_START_DELAY, 252 PhoneStatusBar.FADE_KEYGUARD_DURATION, null); 253 } 254 }, new Runnable() { 255 @Override 256 public void run() { 257 mPhoneStatusBar.hideKeyguard(); 258 mStatusBarWindowManager.setKeyguardFadingAway(false); 259 mViewMediatorCallback.keyguardGone(); 260 executeAfterKeyguardGoneAction(); 261 } 262 }); 263 } else { 264 mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration); 265 boolean staying = mPhoneStatusBar.hideKeyguard(); 266 if (!staying) { 267 mStatusBarWindowManager.setKeyguardFadingAway(true); 268 mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() { 269 @Override 270 public void run() { 271 mStatusBarWindowManager.setKeyguardFadingAway(false); 272 mPhoneStatusBar.finishKeyguardFadingAway(); 273 WindowManagerGlobal.getInstance().trimMemory( 274 ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); 275 } 276 }); 277 } else { 278 mScrimController.animateGoingToFullShade(delay, fadeoutDuration); 279 mPhoneStatusBar.finishKeyguardFadingAway(); 280 } 281 mStatusBarWindowManager.setKeyguardShowing(false); 282 mBouncer.hide(true /* destroyView */); 283 mViewMediatorCallback.keyguardGone(); 284 executeAfterKeyguardGoneAction(); 285 updateStates(); 286 } 287 288 } 289 290 private void executeAfterKeyguardGoneAction() { 291 if (mAfterKeyguardGoneAction != null) { 292 mAfterKeyguardGoneAction.onDismiss(); 293 mAfterKeyguardGoneAction = null; 294 } 295 } 296 297 /** 298 * Dismisses the keyguard by going to the next screen or making it gone. 299 */ 300 public void dismiss() { 301 if (mScreenOn) { 302 showBouncer(); 303 } 304 } 305 306 /** 307 * WARNING: This method might cause Binder calls. 308 */ 309 public boolean isSecure() { 310 return mBouncer.isSecure(); 311 } 312 313 /** 314 * @return Whether the keyguard is showing 315 */ 316 public boolean isShowing() { 317 return mShowing; 318 } 319 320 /** 321 * Notifies this manager that the back button has been pressed. 322 * 323 * @return whether the back press has been handled 324 */ 325 public boolean onBackPressed() { 326 if (mBouncer.isShowing()) { 327 reset(); 328 return true; 329 } 330 return false; 331 } 332 333 public boolean isBouncerShowing() { 334 return mBouncer.isShowing(); 335 } 336 337 private long getNavBarShowDelay() { 338 if (mPhoneStatusBar.isKeyguardFadingAway()) { 339 return mPhoneStatusBar.getKeyguardFadingAwayDelay(); 340 } else { 341 342 // Keyguard is not going away, thus we are showing the navigation bar because the 343 // bouncer is appearing. 344 return NAV_BAR_SHOW_DELAY_BOUNCER; 345 } 346 } 347 348 private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() { 349 @Override 350 public void run() { 351 mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE); 352 } 353 }; 354 355 private void updateStates() { 356 int vis = mContainer.getSystemUiVisibility(); 357 boolean showing = mShowing; 358 boolean occluded = mOccluded; 359 boolean bouncerShowing = mBouncer.isShowing(); 360 boolean bouncerDismissible = !mBouncer.isFullscreenBouncer(); 361 362 if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing) 363 || mFirstUpdate) { 364 if (bouncerDismissible || !showing) { 365 mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK); 366 } else { 367 mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK); 368 } 369 } 370 if ((!(showing && !occluded) || bouncerShowing) 371 != (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing) || mFirstUpdate) { 372 if (mPhoneStatusBar.getNavigationBarView() != null) { 373 if (!(showing && !occluded) || bouncerShowing) { 374 mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable, 375 getNavBarShowDelay()); 376 } else { 377 mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable); 378 mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE); 379 } 380 } 381 } 382 383 if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { 384 mStatusBarWindowManager.setBouncerShowing(bouncerShowing); 385 mPhoneStatusBar.setBouncerShowing(bouncerShowing); 386 mScrimController.setBouncerShowing(bouncerShowing); 387 } 388 389 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 390 if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) { 391 updateMonitor.sendKeyguardVisibilityChanged(showing && !occluded); 392 } 393 if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { 394 updateMonitor.sendKeyguardBouncerChanged(bouncerShowing); 395 } 396 397 mFirstUpdate = false; 398 mLastShowing = showing; 399 mLastOccluded = occluded; 400 mLastBouncerShowing = bouncerShowing; 401 mLastBouncerDismissible = bouncerDismissible; 402 403 mPhoneStatusBar.onKeyguardViewManagerStatesUpdated(); 404 } 405 406 public boolean onMenuPressed() { 407 return mBouncer.onMenuPressed(); 408 } 409 410 public boolean interceptMediaKey(KeyEvent event) { 411 return mBouncer.interceptMediaKey(event); 412 } 413 414 public void onActivityDrawn() { 415 if (mPhoneStatusBar.isCollapsing()) { 416 mPhoneStatusBar.addPostCollapseAction(new Runnable() { 417 @Override 418 public void run() { 419 mViewMediatorCallback.readyForKeyguardDone(); 420 } 421 }); 422 } else { 423 mViewMediatorCallback.readyForKeyguardDone(); 424 } 425 } 426 427 public boolean shouldDisableWindowAnimationsForUnlock() { 428 return mPhoneStatusBar.isInLaunchTransition(); 429 } 430 431 public boolean isGoingToNotificationShade() { 432 return mPhoneStatusBar.isGoingToNotificationShade(); 433 } 434 435 public boolean isSecure(int userId) { 436 return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId); 437 } 438 439 public boolean isInputRestricted() { 440 return mViewMediatorCallback.isInputRestricted(); 441 } 442 } 443