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
17package com.android.systemui.statusbar.phone;
18
19import static com.android.keyguard.KeyguardHostView.OnDismissAction;
20import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
21import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
22
23import android.content.ComponentCallbacks2;
24import android.content.Context;
25import android.os.Bundle;
26import android.os.SystemClock;
27import android.util.StatsLog;
28import android.view.KeyEvent;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.ViewRootImpl;
32import android.view.WindowManagerGlobal;
33
34import com.android.internal.annotations.VisibleForTesting;
35import com.android.internal.util.LatencyTracker;
36import com.android.internal.widget.LockPatternUtils;
37import com.android.keyguard.KeyguardUpdateMonitor;
38import com.android.keyguard.KeyguardUpdateMonitorCallback;
39import com.android.keyguard.ViewMediatorCallback;
40import com.android.systemui.DejankUtils;
41import com.android.systemui.Dependency;
42import com.android.systemui.SystemUIFactory;
43import com.android.systemui.keyguard.DismissCallbackRegistry;
44import com.android.systemui.statusbar.CommandQueue;
45import com.android.systemui.statusbar.RemoteInputController;
46import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
47
48import java.io.PrintWriter;
49import java.util.ArrayList;
50
51/**
52 * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
53 * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
54 * which is in turn, reported to this class by the current
55 * {@link com.android.keyguard.KeyguardViewBase}.
56 */
57public class StatusBarKeyguardViewManager implements RemoteInputController.Callback {
58
59    // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
60    private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
61
62    // Delay for showing the navigation bar when the bouncer appears. This should be kept in sync
63    // with the appear animations of the PIN/pattern/password views.
64    private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
65
66    private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
67
68    // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to
69    // make everything a bit slower to bridge a gap until the user is unlocked and home screen has
70    // dranw its first frame.
71    private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
72
73    private static String TAG = "StatusBarKeyguardViewManager";
74
75    protected final Context mContext;
76    private final StatusBarWindowManager mStatusBarWindowManager;
77    private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
78        @Override
79        public void onFullyShown() {
80            updateStates();
81        }
82
83        @Override
84        public void onFullyHidden() {
85            updateStates();
86        }
87    };
88
89    protected LockPatternUtils mLockPatternUtils;
90    protected ViewMediatorCallback mViewMediatorCallback;
91    protected StatusBar mStatusBar;
92    private NotificationPanelView mNotificationPanelView;
93    private FingerprintUnlockController mFingerprintUnlockController;
94
95    private ViewGroup mContainer;
96
97    protected KeyguardBouncer mBouncer;
98    protected boolean mShowing;
99    protected boolean mOccluded;
100    protected boolean mRemoteInputActive;
101    private boolean mDozing;
102
103    protected boolean mFirstUpdate = true;
104    protected boolean mLastShowing;
105    protected boolean mLastOccluded;
106    private boolean mLastBouncerShowing;
107    private boolean mLastBouncerDismissible;
108    protected boolean mLastRemoteInputActive;
109    private boolean mLastDozing;
110    private int mLastFpMode;
111    private boolean mGoingToSleepVisibleNotOccluded;
112
113    private OnDismissAction mAfterKeyguardGoneAction;
114    private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
115
116    // Dismiss action to be launched when we stop dozing or the keyguard is gone.
117    private DismissWithActionRequest mPendingWakeupAction;
118
119    private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
120            new KeyguardUpdateMonitorCallback() {
121        @Override
122        public void onEmergencyCallAction() {
123
124            // Since we won't get a setOccluded call we have to reset the view manually such that
125            // the bouncer goes away.
126            if (mOccluded) {
127                reset(true /* hideBouncerWhenShowing */);
128            }
129        }
130    };
131
132    public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
133            LockPatternUtils lockPatternUtils) {
134        mContext = context;
135        mViewMediatorCallback = callback;
136        mLockPatternUtils = lockPatternUtils;
137        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
138        KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
139    }
140
141    public void registerStatusBar(StatusBar statusBar,
142            ViewGroup container,
143            NotificationPanelView notificationPanelView,
144            FingerprintUnlockController fingerprintUnlockController,
145            DismissCallbackRegistry dismissCallbackRegistry) {
146        mStatusBar = statusBar;
147        mContainer = container;
148        mFingerprintUnlockController = fingerprintUnlockController;
149        mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
150                mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
151                mExpansionCallback);
152        mContainer.addOnLayoutChangeListener(this::onContainerLayout);
153        mNotificationPanelView = notificationPanelView;
154        notificationPanelView.setExpansionListener(this::onPanelExpansionChanged);
155    }
156
157    private void onContainerLayout(View v, int left, int top, int right, int bottom,
158            int oldLeft, int oldTop, int oldRight, int oldBottom) {
159        mNotificationPanelView.setBouncerTop(mBouncer.getTop());
160    }
161
162    @VisibleForTesting
163    void onPanelExpansionChanged(float expansion, boolean tracking) {
164        // We don't want to translate the bounce when:
165        // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
166        //   conserve the original animation.
167        // • The user quickly taps on the display and we show "swipe up to unlock."
168        // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
169        // • Full-screen user switcher is displayed.
170        if (mNotificationPanelView.isUnlockHintRunning()) {
171            mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
172        } else if (mOccluded || mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed()
173                || mStatusBar.isFullScreenUserSwitcherState()) {
174            mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
175        } else if (mShowing && !mDozing) {
176            if (!isWakeAndUnlocking()) {
177                mBouncer.setExpansion(expansion);
178            }
179            if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking
180                    && mStatusBar.isKeyguardCurrentlySecure()
181                    && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
182                mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
183            }
184        }
185    }
186
187    /**
188     * Show the keyguard.  Will handle creating and attaching to the view manager
189     * lazily.
190     */
191    public void show(Bundle options) {
192        mShowing = true;
193        mStatusBarWindowManager.setKeyguardShowing(true);
194        reset(true /* hideBouncerWhenShowing */);
195        StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
196            StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
197    }
198
199    /**
200     * Shows the notification keyguard or the bouncer depending on
201     * {@link KeyguardBouncer#needsFullscreenBouncer()}.
202     */
203    protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
204        if (mBouncer.needsFullscreenBouncer() && !mDozing) {
205            // The keyguard might be showing (already). So we need to hide it.
206            mStatusBar.hideKeyguard();
207            mBouncer.show(true /* resetSecuritySelection */);
208        } else {
209            mStatusBar.showKeyguard();
210            if (hideBouncerWhenShowing) {
211                hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
212                mBouncer.prepare();
213            }
214        }
215        updateStates();
216    }
217
218    protected boolean shouldDestroyViewOnReset() {
219        return false;
220    }
221
222    private void hideBouncer(boolean destroyView) {
223        mBouncer.hide(destroyView);
224        cancelPendingWakeupAction();
225    }
226
227    public void showBouncer(boolean scrimmed) {
228        if (mShowing && !mBouncer.isShowing()) {
229            mBouncer.show(false /* resetSecuritySelection */, scrimmed);
230        }
231        updateStates();
232    }
233
234    public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
235            boolean afterKeyguardGone) {
236        dismissWithAction(r, cancelAction, afterKeyguardGone, null /* message */);
237    }
238
239    public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
240            boolean afterKeyguardGone, String message) {
241        if (mShowing) {
242            cancelPendingWakeupAction();
243            // If we're dozing, this needs to be delayed until after we wake up - unless we're
244            // wake-and-unlocking, because there dozing will last until the end of the transition.
245            if (mDozing && !isWakeAndUnlocking()) {
246                mPendingWakeupAction = new DismissWithActionRequest(
247                        r, cancelAction, afterKeyguardGone, message);
248                return;
249            }
250
251            if (!afterKeyguardGone) {
252                mBouncer.showWithDismissAction(r, cancelAction);
253            } else {
254                mAfterKeyguardGoneAction = r;
255                mBouncer.show(false /* resetSecuritySelection */);
256            }
257        }
258        updateStates();
259    }
260
261    private boolean isWakeAndUnlocking() {
262        int mode = mFingerprintUnlockController.getMode();
263        return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING;
264    }
265
266    /**
267     * Adds a {@param runnable} to be executed after Keyguard is gone.
268     */
269    public void addAfterKeyguardGoneRunnable(Runnable runnable) {
270        mAfterKeyguardGoneRunnables.add(runnable);
271    }
272
273    /**
274     * Reset the state of the view.
275     */
276    public void reset(boolean hideBouncerWhenShowing) {
277        if (mShowing) {
278            if (mOccluded && !mDozing) {
279                mStatusBar.hideKeyguard();
280                if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
281                    hideBouncer(false /* destroyView */);
282                }
283            } else {
284                showBouncerOrKeyguard(hideBouncerWhenShowing);
285            }
286            KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
287            updateStates();
288        }
289    }
290
291    public boolean isGoingToSleepVisibleNotOccluded() {
292        return mGoingToSleepVisibleNotOccluded;
293    }
294
295    public void onStartedGoingToSleep() {
296        mGoingToSleepVisibleNotOccluded = isShowing() && !isOccluded();
297    }
298
299    public void onFinishedGoingToSleep() {
300        mGoingToSleepVisibleNotOccluded = false;
301        mBouncer.onScreenTurnedOff();
302    }
303
304    public void onStartedWakingUp() {
305        // TODO: remove
306    }
307
308    public void onScreenTurningOn() {
309        // TODO: remove
310    }
311
312    public void onScreenTurnedOn() {
313        // TODO: remove
314    }
315
316    @Override
317    public void onRemoteInputActive(boolean active) {
318        mRemoteInputActive = active;
319        updateStates();
320    }
321
322    public void setDozing(boolean dozing) {
323        if (mDozing != dozing) {
324            mDozing = dozing;
325            if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
326                reset(dozing /* hideBouncerWhenShowing */);
327            }
328            updateStates();
329
330            if (!dozing) {
331                launchPendingWakeupAction();
332            }
333        }
334    }
335
336    public void onScreenTurnedOff() {
337        // TODO: remove
338    }
339
340    public void notifyDeviceWakeUpRequested() {
341        // TODO: remove
342    }
343
344    public void setNeedsInput(boolean needsInput) {
345        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
346    }
347
348    public boolean isUnlockWithWallpaper() {
349        return mStatusBarWindowManager.isShowingWallpaper();
350    }
351
352    public void setOccluded(boolean occluded, boolean animate) {
353        mStatusBar.setOccluded(occluded);
354        if (occluded && !mOccluded && mShowing) {
355            StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
356                StatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
357            if (mStatusBar.isInLaunchTransition()) {
358                mOccluded = true;
359                mStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
360                        new Runnable() {
361                            @Override
362                            public void run() {
363                                mStatusBarWindowManager.setKeyguardOccluded(mOccluded);
364                                reset(true /* hideBouncerWhenShowing */);
365                            }
366                        });
367                return;
368            }
369        } else if (!occluded && mOccluded && mShowing) {
370            StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
371                StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
372        }
373        boolean isOccluding = !mOccluded && occluded;
374        mOccluded = occluded;
375        if (mShowing) {
376            mStatusBar.updateMediaMetaData(false, animate && !occluded);
377        }
378        mStatusBarWindowManager.setKeyguardOccluded(occluded);
379
380        // setDozing(false) will call reset once we stop dozing.
381        if (!mDozing) {
382            // If Keyguard is reshown, don't hide the bouncer as it might just have been requested
383            // by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
384            reset(isOccluding /* hideBouncerWhenShowing*/);
385        }
386        if (animate && !occluded && mShowing) {
387            mStatusBar.animateKeyguardUnoccluding();
388        }
389    }
390
391    public boolean isOccluded() {
392        return mOccluded;
393    }
394
395    /**
396     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
397     * security view of the bouncer.
398     *
399     * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
400     *                       no action should be run
401     */
402    public void startPreHideAnimation(Runnable finishRunnable) {
403        if (mBouncer.isShowing()) {
404            mBouncer.startPreHideAnimation(finishRunnable);
405            mNotificationPanelView.onBouncerPreHideAnimation();
406        } else if (finishRunnable != null) {
407            finishRunnable.run();
408        }
409    }
410
411    /**
412     * Hides the keyguard view
413     */
414    public void hide(long startTime, long fadeoutDuration) {
415        mShowing = false;
416        launchPendingWakeupAction();
417
418        if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
419            fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
420        }
421        long uptimeMillis = SystemClock.uptimeMillis();
422        long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
423
424        if (mStatusBar.isInLaunchTransition() ) {
425            mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
426                @Override
427                public void run() {
428                    mStatusBarWindowManager.setKeyguardShowing(false);
429                    mStatusBarWindowManager.setKeyguardFadingAway(true);
430                    hideBouncer(true /* destroyView */);
431                    updateStates();
432                }
433            }, new Runnable() {
434                @Override
435                public void run() {
436                    mStatusBar.hideKeyguard();
437                    mStatusBarWindowManager.setKeyguardFadingAway(false);
438                    mViewMediatorCallback.keyguardGone();
439                    executeAfterKeyguardGoneAction();
440                }
441            });
442        } else {
443            executeAfterKeyguardGoneAction();
444            boolean wakeUnlockPulsing =
445                    mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
446            if (wakeUnlockPulsing) {
447                delay = 0;
448                fadeoutDuration = 240;
449            }
450            mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
451            mFingerprintUnlockController.startKeyguardFadingAway();
452            hideBouncer(true /* destroyView */);
453            if (wakeUnlockPulsing) {
454                mStatusBar.fadeKeyguardWhilePulsing();
455                wakeAndUnlockDejank();
456            } else {
457                boolean staying = mStatusBar.hideKeyguard();
458                if (!staying) {
459                    mStatusBarWindowManager.setKeyguardFadingAway(true);
460                    wakeAndUnlockDejank();
461                } else {
462                    mStatusBar.finishKeyguardFadingAway();
463                    mFingerprintUnlockController.finishKeyguardFadingAway();
464                }
465            }
466            updateStates();
467            mStatusBarWindowManager.setKeyguardShowing(false);
468            mViewMediatorCallback.keyguardGone();
469        }
470        StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
471            StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
472    }
473
474    public void onDensityOrFontScaleChanged() {
475        hideBouncer(true /* destroyView */);
476    }
477
478    public void onThemeChanged() {
479        hideBouncer(true /* destroyView */);
480        mBouncer.prepare();
481    }
482
483    public void onKeyguardFadedAway() {
484        mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
485                100);
486        mStatusBar.finishKeyguardFadingAway();
487        mFingerprintUnlockController.finishKeyguardFadingAway();
488        WindowManagerGlobal.getInstance().trimMemory(
489                ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
490
491    }
492
493    private void wakeAndUnlockDejank() {
494        if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
495                && LatencyTracker.isEnabled(mContext)) {
496            DejankUtils.postAfterTraversal(() ->
497                    LatencyTracker.getInstance(mContext).onActionEnd(
498                            LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK));
499        }
500    }
501
502    private void executeAfterKeyguardGoneAction() {
503        if (mAfterKeyguardGoneAction != null) {
504            mAfterKeyguardGoneAction.onDismiss();
505            mAfterKeyguardGoneAction = null;
506        }
507        for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
508            mAfterKeyguardGoneRunnables.get(i).run();
509        }
510        mAfterKeyguardGoneRunnables.clear();
511    }
512
513    /**
514     * Dismisses the keyguard by going to the next screen or making it gone.
515     */
516    public void dismissAndCollapse() {
517        mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
518    }
519
520    /**
521     * WARNING: This method might cause Binder calls.
522     */
523    public boolean isSecure() {
524        return mBouncer.isSecure();
525    }
526
527    /**
528     * @return Whether the keyguard is showing
529     */
530    public boolean isShowing() {
531        return mShowing;
532    }
533
534    /**
535     * Notifies this manager that the back button has been pressed.
536     *
537     * @param hideImmediately Hide bouncer when {@code true}, keep it around otherwise.
538     *                        Non-scrimmed bouncers have a special animation tied to the expansion
539     *                        of the notification panel.
540     * @return whether the back press has been handled
541     */
542    public boolean onBackPressed(boolean hideImmediately) {
543        if (mBouncer.isShowing()) {
544            mStatusBar.endAffordanceLaunch();
545            reset(hideImmediately);
546            return true;
547        }
548        return false;
549    }
550
551    public boolean isBouncerShowing() {
552        return mBouncer.isShowing();
553    }
554
555    public boolean isFullscreenBouncer() {
556        return mBouncer.isFullscreenBouncer();
557    }
558
559    private long getNavBarShowDelay() {
560        if (mStatusBar.isKeyguardFadingAway()) {
561            return mStatusBar.getKeyguardFadingAwayDelay();
562        } else if (mBouncer.isShowing()) {
563            return NAV_BAR_SHOW_DELAY_BOUNCER;
564        } else {
565            // No longer dozing, or remote input is active. No delay.
566            return 0;
567        }
568    }
569
570    private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
571        @Override
572        public void run() {
573            mStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
574        }
575    };
576
577    protected void updateStates() {
578        int vis = mContainer.getSystemUiVisibility();
579        boolean showing = mShowing;
580        boolean occluded = mOccluded;
581        boolean bouncerShowing = mBouncer.isShowing();
582        boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
583        boolean remoteInputActive = mRemoteInputActive;
584
585        if ((bouncerDismissible || !showing || remoteInputActive) !=
586                (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
587                || mFirstUpdate) {
588            if (bouncerDismissible || !showing || remoteInputActive) {
589                mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
590            } else {
591                mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
592            }
593        }
594
595        boolean navBarVisible = isNavBarVisible();
596        boolean lastNavBarVisible = getLastNavBarVisible();
597        if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
598            updateNavigationBarVisibility(navBarVisible);
599        }
600
601        if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
602            mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
603            mStatusBar.setBouncerShowing(bouncerShowing);
604        }
605
606        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
607        if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
608            updateMonitor.onKeyguardVisibilityChanged(showing && !occluded);
609        }
610        if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
611            updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
612        }
613
614        mFirstUpdate = false;
615        mLastShowing = showing;
616        mLastOccluded = occluded;
617        mLastBouncerShowing = bouncerShowing;
618        mLastBouncerDismissible = bouncerDismissible;
619        mLastRemoteInputActive = remoteInputActive;
620        mLastDozing = mDozing;
621        mLastFpMode = mFingerprintUnlockController.getMode();
622        mStatusBar.onKeyguardViewManagerStatesUpdated();
623    }
624
625    protected void updateNavigationBarVisibility(boolean navBarVisible) {
626        if (mStatusBar.getNavigationBarView() != null) {
627            if (navBarVisible) {
628                long delay = getNavBarShowDelay();
629                if (delay == 0) {
630                    mMakeNavigationBarVisibleRunnable.run();
631                } else {
632                    mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
633                            delay);
634                }
635            } else {
636                mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
637                mStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
638            }
639        }
640    }
641
642    /**
643     * @return Whether the navigation bar should be made visible based on the current state.
644     */
645    protected boolean isNavBarVisible() {
646        int fpMode = mFingerprintUnlockController.getMode();
647        boolean keyguardShowing = mShowing && !mOccluded;
648        boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
649        return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
650                || mRemoteInputActive);
651    }
652
653    /**
654     * @return Whether the navigation bar was made visible based on the last known state.
655     */
656    protected boolean getLastNavBarVisible() {
657        boolean keyguardShowing = mLastShowing && !mLastOccluded;
658        boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
659        return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
660                || mLastRemoteInputActive);
661    }
662
663    public boolean shouldDismissOnMenuPressed() {
664        return mBouncer.shouldDismissOnMenuPressed();
665    }
666
667    public boolean interceptMediaKey(KeyEvent event) {
668        return mBouncer.interceptMediaKey(event);
669    }
670
671    public void readyForKeyguardDone() {
672        mViewMediatorCallback.readyForKeyguardDone();
673    }
674
675    public boolean shouldDisableWindowAnimationsForUnlock() {
676        return mStatusBar.isInLaunchTransition();
677    }
678
679    public boolean isGoingToNotificationShade() {
680        return mStatusBar.isGoingToNotificationShade();
681    }
682
683    public boolean isSecure(int userId) {
684        return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
685    }
686
687    public void keyguardGoingAway() {
688        mStatusBar.keyguardGoingAway();
689    }
690
691    public void animateCollapsePanels(float speedUpFactor) {
692        mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
693                false /* delayed */, speedUpFactor);
694    }
695
696    /**
697     * Notifies that the user has authenticated by other means than using the bouncer, for example,
698     * fingerprint.
699     */
700    public void notifyKeyguardAuthenticated(boolean strongAuth) {
701        mBouncer.notifyKeyguardAuthenticated(strongAuth);
702    }
703
704    public void showBouncerMessage(String message, int color) {
705        mBouncer.showMessage(message, color);
706    }
707
708    public ViewRootImpl getViewRootImpl() {
709        return mStatusBar.getStatusBarView().getViewRootImpl();
710    }
711
712    public void launchPendingWakeupAction() {
713        DismissWithActionRequest request = mPendingWakeupAction;
714        mPendingWakeupAction = null;
715        if (request != null) {
716            if (mShowing) {
717                dismissWithAction(request.dismissAction, request.cancelAction,
718                        request.afterKeyguardGone, request.message);
719            } else if (request.dismissAction != null) {
720                request.dismissAction.onDismiss();
721            }
722        }
723    }
724
725    public void cancelPendingWakeupAction() {
726        DismissWithActionRequest request = mPendingWakeupAction;
727        mPendingWakeupAction = null;
728        if (request != null && request.cancelAction != null) {
729            request.cancelAction.run();
730        }
731    }
732
733    public boolean willDismissWithAction() {
734        return mBouncer.willDismissWithAction();
735    }
736
737    public boolean bouncerNeedsScrimming() {
738        return mBouncer.isShowingScrimmed();
739    }
740
741    public void dump(PrintWriter pw) {
742        pw.println("StatusBarKeyguardViewManager:");
743        pw.println("  mShowing: " + mShowing);
744        pw.println("  mOccluded: " + mOccluded);
745        pw.println("  mRemoteInputActive: " + mRemoteInputActive);
746        pw.println("  mDozing: " + mDozing);
747        pw.println("  mGoingToSleepVisibleNotOccluded: " + mGoingToSleepVisibleNotOccluded);
748        pw.println("  mAfterKeyguardGoneAction: " + mAfterKeyguardGoneAction);
749        pw.println("  mAfterKeyguardGoneRunnables: " + mAfterKeyguardGoneRunnables);
750        pw.println("  mPendingWakeupAction: " + mPendingWakeupAction);
751
752        if (mBouncer != null) {
753            mBouncer.dump(pw);
754        }
755    }
756
757    private static class DismissWithActionRequest {
758        final OnDismissAction dismissAction;
759        final Runnable cancelAction;
760        final boolean afterKeyguardGone;
761        final String message;
762
763        DismissWithActionRequest(OnDismissAction dismissAction, Runnable cancelAction,
764                boolean afterKeyguardGone, String message) {
765            this.dismissAction = dismissAction;
766            this.cancelAction = cancelAction;
767            this.afterKeyguardGone = afterKeyguardGone;
768            this.message = message;
769        }
770    }
771}
772