• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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