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