[go: nahoru, domu]

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.ViewRootImpl;
28import android.view.WindowManagerGlobal;
29
30import com.android.internal.widget.LockPatternUtils;
31import com.android.keyguard.KeyguardUpdateMonitor;
32import com.android.keyguard.ViewMediatorCallback;
33import com.android.systemui.SystemUIFactory;
34import com.android.systemui.statusbar.CommandQueue;
35import com.android.systemui.statusbar.RemoteInputController;
36
37import 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 */
45public 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
87    public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
88            LockPatternUtils lockPatternUtils) {
89        mContext = context;
90        mViewMediatorCallback = callback;
91        mLockPatternUtils = lockPatternUtils;
92    }
93
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     */
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     */
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
135    private void showBouncer() {
136        if (mShowing) {
137            mBouncer.show(false /* resetSecuritySelection */);
138        }
139        updateStates();
140    }
141
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     */
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
172    public void onStartedGoingToSleep() {
173        mPhoneStatusBar.onStartedGoingToSleep();
174    }
175
176    public void onFinishedGoingToSleep() {
177        mDeviceInteractive = false;
178        mPhoneStatusBar.onFinishedGoingToSleep();
179        mBouncer.onScreenTurnedOff();
180    }
181
182    public void onStartedWakingUp() {
183        mDeviceInteractive = true;
184        mDeviceWillWakeUp = false;
185        mPhoneStatusBar.onStartedWakingUp();
186    }
187
188    public void onScreenTurningOn() {
189        mPhoneStatusBar.onScreenTurningOn();
190    }
191
192    public boolean isScreenTurnedOn() {
193        return mScreenTurnedOn;
194    }
195
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
208    public void onRemoteInputActive(boolean active) {
209        mRemoteInputActive = active;
210        updateStates();
211    }
212
213    public void onScreenTurnedOff() {
214        mScreenTurnedOn = false;
215        mPhoneStatusBar.onScreenTurnedOff();
216    }
217
218    public void notifyDeviceWakeUpRequested() {
219        mDeviceWillWakeUp = !mDeviceInteractive;
220    }
221
222    public void verifyUnlock() {
223        dismiss();
224    }
225
226    public void setNeedsInput(boolean needsInput) {
227        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
228    }
229
230    public boolean isUnlockWithWallpaper() {
231        return mStatusBarWindowManager.isShowingWallpaper();
232    }
233
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
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     */
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     */
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
352    public void onDensityOrFontScaleChanged() {
353        mBouncer.hide(true /* destroyView */);
354    }
355
356    private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
357            boolean skipFirstFrame) {
358        animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
359                skipFirstFrame);
360    }
361
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
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     */
391    public void dismiss() {
392        if (mDeviceInteractive || mDeviceWillWakeUp) {
393            showBouncer();
394        }
395    }
396
397    /**
398     * WARNING: This method might cause Binder calls.
399     */
400    public boolean isSecure() {
401        return mBouncer.isSecure();
402    }
403
404    /**
405     * @return Whether the keyguard is showing
406     */
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     */
416    public boolean onBackPressed() {
417        if (mBouncer.isShowing()) {
418            mPhoneStatusBar.endAffordanceLaunch();
419            reset();
420            return true;
421        }
422        return false;
423    }
424
425    public boolean isBouncerShowing() {
426        return mBouncer.isShowing();
427    }
428
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
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     */
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     */
518    protected boolean getLastNavBarVisible() {
519        return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive;
520    }
521
522    public boolean shouldDismissOnMenuPressed() {
523        return mBouncer.shouldDismissOnMenuPressed();
524    }
525
526    public boolean interceptMediaKey(KeyEvent event) {
527        return mBouncer.interceptMediaKey(event);
528    }
529
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
543    public boolean shouldDisableWindowAnimationsForUnlock() {
544        return mPhoneStatusBar.isInLaunchTransition();
545    }
546
547    public boolean isGoingToNotificationShade() {
548        return mPhoneStatusBar.isGoingToNotificationShade();
549    }
550
551    public boolean isSecure(int userId) {
552        return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
553    }
554
555    public boolean isInputRestricted() {
556        return mViewMediatorCallback.isInputRestricted();
557    }
558
559    public void keyguardGoingAway() {
560        mPhoneStatusBar.keyguardGoingAway();
561    }
562
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     */
572    public void notifyKeyguardAuthenticated(boolean strongAuth) {
573        mBouncer.notifyKeyguardAuthenticated(strongAuth);
574    }
575
576    public void showBouncerMessage(String message, int color) {
577        mBouncer.showMessage(message, color);
578    }
579
580    public ViewRootImpl getViewRootImpl() {
581        return mPhoneStatusBar.getStatusBarView().getViewRootImpl();
582    }
583}
584