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 android.content.ComponentCallbacks2;
20import android.content.Context;
21import android.os.Bundle;
22import android.os.SystemClock;
23import android.os.Trace;
24import android.view.KeyEvent;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.WindowManagerGlobal;
28
29import com.android.internal.widget.LockPatternUtils;
30import com.android.keyguard.KeyguardUpdateMonitor;
31import com.android.keyguard.ViewMediatorCallback;
32import com.android.systemui.statusbar.CommandQueue;
33
34import 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 */
42public 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
80    public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
81            LockPatternUtils lockPatternUtils) {
82        mContext = context;
83        mViewMediatorCallback = callback;
84        mLockPatternUtils = lockPatternUtils;
85    }
86
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     */
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     */
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
126    private void showBouncer() {
127        if (mShowing) {
128            mBouncer.show(false /* resetSecuritySelection */);
129        }
130        updateStates();
131    }
132
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     */
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
163    public void onFinishedGoingToSleep() {
164        mDeviceInteractive = false;
165        mPhoneStatusBar.onScreenTurnedOff();
166        mBouncer.onScreenTurnedOff();
167    }
168
169    public void onStartedWakingUp() {
170        mDeviceInteractive = true;
171        mDeviceWillWakeUp = false;
172        mPhoneStatusBar.onScreenTurnedOn();
173    }
174
175    public void onScreenTurningOn() {
176        mPhoneStatusBar.onScreenTurningOn();
177    }
178
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
189    public void onScreenTurnedOff() {
190        mScreenTurnedOn = false;
191    }
192
193    public void notifyDeviceWakeUpRequested() {
194        mDeviceWillWakeUp = !mDeviceInteractive;
195    }
196
197    public void verifyUnlock() {
198        dismiss();
199    }
200
201    public void setNeedsInput(boolean needsInput) {
202        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
203    }
204
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
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     */
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     */
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
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
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     */
324    public void dismiss() {
325        if (mDeviceInteractive || mDeviceWillWakeUp) {
326            showBouncer();
327        }
328    }
329
330    /**
331     * WARNING: This method might cause Binder calls.
332     */
333    public boolean isSecure() {
334        return mBouncer.isSecure();
335    }
336
337    /**
338     * @return Whether the keyguard is showing
339     */
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     */
349    public boolean onBackPressed() {
350        if (mBouncer.isShowing()) {
351            reset();
352            return true;
353        }
354        return false;
355    }
356
357    public boolean isBouncerShowing() {
358        return mBouncer.isShowing();
359    }
360
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
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
437    public boolean onMenuPressed() {
438        return mBouncer.onMenuPressed();
439    }
440
441    public boolean interceptMediaKey(KeyEvent event) {
442        return mBouncer.interceptMediaKey(event);
443    }
444
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
458    public boolean shouldDisableWindowAnimationsForUnlock() {
459        return mPhoneStatusBar.isInLaunchTransition();
460    }
461
462    public boolean isGoingToNotificationShade() {
463        return mPhoneStatusBar.isGoingToNotificationShade();
464    }
465
466    public boolean isSecure(int userId) {
467        return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
468    }
469
470    public boolean isInputRestricted() {
471        return mViewMediatorCallback.isInputRestricted();
472    }
473
474    public void keyguardGoingAway() {
475        mPhoneStatusBar.keyguardGoingAway();
476    }
477
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     */
487    public void notifyKeyguardAuthenticated() {
488        mBouncer.notifyKeyguardAuthenticated();
489    }
490
491    public void setWakeAndUnlocking() {
492        mWakeAndUnlocking = true;
493        mScrimController.setWakeAndUnlocking();
494        if (mPhoneStatusBar.getNavigationBarView() != null) {
495            mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
496        }
497    }
498}
499