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