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