StatusBarKeyguardViewManager.java revision 25d7e51a10828e4433fcca90410d13a4b2342c34
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.RemoteException;
23import android.os.SystemClock;
24import android.util.Slog;
25import android.view.KeyEvent;
26import android.view.View;
27import android.view.ViewGroup;
28import android.view.WindowManagerGlobal;
29
30import com.android.internal.policy.IKeyguardShowCallback;
31import com.android.internal.widget.LockPatternUtils;
32import com.android.keyguard.KeyguardUpdateMonitor;
33import com.android.keyguard.ViewMediatorCallback;
34import com.android.systemui.statusbar.CommandQueue;
35
36import static com.android.keyguard.KeyguardHostView.OnDismissAction;
37
38/**
39 * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
40 * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
41 * which is in turn, reported to this class by the current
42 * {@link com.android.keyguard.KeyguardViewBase}.
43 */
44public class StatusBarKeyguardViewManager {
45
46    // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
47    private static final long HIDE_TIMING_CORRECTION_MS = -3 * 16;
48
49    // Delay for showing the navigation bar when the bouncer appears. This should be kept in sync
50    // with the appear animations of the PIN/pattern/password views.
51    private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
52
53    private static String TAG = "StatusBarKeyguardViewManager";
54
55    private final Context mContext;
56
57    private LockPatternUtils mLockPatternUtils;
58    private ViewMediatorCallback mViewMediatorCallback;
59    private PhoneStatusBar mPhoneStatusBar;
60    private ScrimController mScrimController;
61
62    private ViewGroup mContainer;
63    private StatusBarWindowManager mStatusBarWindowManager;
64
65    private boolean mScreenOn = false;
66    private KeyguardBouncer mBouncer;
67    private boolean mShowing;
68    private boolean mOccluded;
69
70    private boolean mFirstUpdate = true;
71    private boolean mLastShowing;
72    private boolean mLastOccluded;
73    private boolean mLastBouncerShowing;
74    private boolean mLastBouncerDismissible;
75    private OnDismissAction mAfterKeyguardGoneAction;
76
77    public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
78            LockPatternUtils lockPatternUtils) {
79        mContext = context;
80        mViewMediatorCallback = callback;
81        mLockPatternUtils = lockPatternUtils;
82    }
83
84    public void registerStatusBar(PhoneStatusBar phoneStatusBar,
85            ViewGroup container, StatusBarWindowManager statusBarWindowManager,
86            ScrimController scrimController) {
87        mPhoneStatusBar = phoneStatusBar;
88        mContainer = container;
89        mStatusBarWindowManager = statusBarWindowManager;
90        mScrimController = scrimController;
91        mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
92                mStatusBarWindowManager, container);
93    }
94
95    /**
96     * Show the keyguard.  Will handle creating and attaching to the view manager
97     * lazily.
98     */
99    public void show(Bundle options) {
100        mShowing = true;
101        mStatusBarWindowManager.setKeyguardShowing(true);
102        reset();
103    }
104
105    /**
106     * Shows the notification keyguard or the bouncer depending on
107     * {@link KeyguardBouncer#needsFullscreenBouncer()}.
108     */
109    private void showBouncerOrKeyguard() {
110        if (mBouncer.needsFullscreenBouncer()) {
111
112            // The keyguard might be showing (already). So we need to hide it.
113            mPhoneStatusBar.hideKeyguard();
114            mBouncer.show(true /* resetSecuritySelection */);
115        } else {
116            mPhoneStatusBar.showKeyguard();
117            mBouncer.hide(false /* destroyView */);
118            mBouncer.prepare();
119        }
120    }
121
122    private void showBouncer() {
123        if (mShowing) {
124            mBouncer.show(false /* resetSecuritySelection */);
125        }
126        updateStates();
127    }
128
129    public void dismissWithAction(OnDismissAction r, boolean afterKeyguardGone) {
130        if (mShowing) {
131            if (!afterKeyguardGone) {
132                mBouncer.showWithDismissAction(r);
133            } else {
134                mBouncer.show(false /* resetSecuritySelection */);
135                mAfterKeyguardGoneAction = r;
136            }
137        }
138        updateStates();
139    }
140
141    /**
142     * Reset the state of the view.
143     */
144    public void reset() {
145        if (mShowing) {
146            if (mOccluded) {
147                mPhoneStatusBar.hideKeyguard();
148                mBouncer.hide(false /* destroyView */);
149            } else {
150                showBouncerOrKeyguard();
151            }
152            updateStates();
153        }
154    }
155
156    public void onScreenTurnedOff() {
157        mScreenOn = false;
158        mPhoneStatusBar.onScreenTurnedOff();
159        mBouncer.onScreenTurnedOff();
160    }
161
162    public void onScreenTurnedOn(final IKeyguardShowCallback callback) {
163        mScreenOn = true;
164        mPhoneStatusBar.onScreenTurnedOn();
165        if (callback != null) {
166            callbackAfterDraw(callback);
167        }
168    }
169
170    private void callbackAfterDraw(final IKeyguardShowCallback callback) {
171        mContainer.post(new Runnable() {
172            @Override
173            public void run() {
174                try {
175                    callback.onShown(mContainer.getWindowToken());
176                } catch (RemoteException e) {
177                    Slog.w(TAG, "Exception calling onShown():", e);
178                }
179            }
180        });
181    }
182
183    public void verifyUnlock() {
184        dismiss();
185    }
186
187    public void setNeedsInput(boolean needsInput) {
188        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
189    }
190
191    public void setOccluded(boolean occluded) {
192        if (occluded && !mOccluded && mShowing) {
193            if (mPhoneStatusBar.isInLaunchTransition()) {
194                mOccluded = true;
195                mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
196                        new Runnable() {
197                            @Override
198                            public void run() {
199                                mStatusBarWindowManager.setKeyguardOccluded(mOccluded);
200                                reset();
201                            }
202                        });
203                return;
204            }
205        }
206        mOccluded = occluded;
207        mStatusBarWindowManager.setKeyguardOccluded(occluded);
208        reset();
209    }
210
211    public boolean isOccluded() {
212        return mOccluded;
213    }
214
215    /**
216     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
217     * security view of the bouncer.
218     *
219     * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
220     *                       no action should be run
221     */
222    public void startPreHideAnimation(Runnable finishRunnable) {
223        if (mBouncer.isShowing()) {
224            mBouncer.startPreHideAnimation(finishRunnable);
225        } else if (finishRunnable != null) {
226            finishRunnable.run();
227        }
228    }
229
230    /**
231     * Hides the keyguard view
232     */
233    public void hide(long startTime, final long fadeoutDuration) {
234        mShowing = false;
235
236        long uptimeMillis = SystemClock.uptimeMillis();
237        long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
238
239        if (mPhoneStatusBar.isInLaunchTransition() ) {
240            mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
241                @Override
242                public void run() {
243                    mStatusBarWindowManager.setKeyguardShowing(false);
244                    mStatusBarWindowManager.setKeyguardFadingAway(true);
245                    mBouncer.hide(true /* destroyView */);
246                    updateStates();
247                    mScrimController.animateKeyguardFadingOut(
248                            PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
249                            PhoneStatusBar.FADE_KEYGUARD_DURATION, null);
250                }
251            }, new Runnable() {
252                @Override
253                public void run() {
254                    mPhoneStatusBar.hideKeyguard();
255                    mStatusBarWindowManager.setKeyguardFadingAway(false);
256                    mViewMediatorCallback.keyguardGone();
257                    executeAfterKeyguardGoneAction();
258                }
259            });
260        } else {
261            mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
262            boolean staying = mPhoneStatusBar.hideKeyguard();
263            if (!staying) {
264                mStatusBarWindowManager.setKeyguardFadingAway(true);
265                mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
266                    @Override
267                    public void run() {
268                        mStatusBarWindowManager.setKeyguardFadingAway(false);
269                        mPhoneStatusBar.finishKeyguardFadingAway();
270                        WindowManagerGlobal.getInstance().trimMemory(
271                                ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
272                    }
273                });
274            } else {
275                mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
276                mPhoneStatusBar.finishKeyguardFadingAway();
277            }
278            mStatusBarWindowManager.setKeyguardShowing(false);
279            mBouncer.hide(true /* destroyView */);
280            mViewMediatorCallback.keyguardGone();
281            executeAfterKeyguardGoneAction();
282            updateStates();
283        }
284
285    }
286
287    private void executeAfterKeyguardGoneAction() {
288        if (mAfterKeyguardGoneAction != null) {
289            mAfterKeyguardGoneAction.onDismiss();
290            mAfterKeyguardGoneAction = null;
291        }
292    }
293
294    /**
295     * Dismisses the keyguard by going to the next screen or making it gone.
296     */
297    public void dismiss() {
298        if (mScreenOn) {
299            showBouncer();
300        }
301    }
302
303    /**
304     * WARNING: This method might cause Binder calls.
305     */
306    public boolean isSecure() {
307        return mBouncer.isSecure();
308    }
309
310    /**
311     * @return Whether the keyguard is showing
312     */
313    public boolean isShowing() {
314        return mShowing;
315    }
316
317    /**
318     * Notifies this manager that the back button has been pressed.
319     *
320     * @return whether the back press has been handled
321     */
322    public boolean onBackPressed() {
323        if (mBouncer.isShowing()) {
324            reset();
325            return true;
326        }
327        return false;
328    }
329
330    public boolean isBouncerShowing() {
331        return mBouncer.isShowing();
332    }
333
334    private long getNavBarShowDelay() {
335        if (mPhoneStatusBar.isKeyguardFadingAway()) {
336            return mPhoneStatusBar.getKeyguardFadingAwayDelay();
337        } else {
338
339            // Keyguard is not going away, thus we are showing the navigation bar because the
340            // bouncer is appearing.
341            return NAV_BAR_SHOW_DELAY_BOUNCER;
342        }
343    }
344
345    private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
346        @Override
347        public void run() {
348            mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE);
349        }
350    };
351
352    private void updateStates() {
353        int vis = mContainer.getSystemUiVisibility();
354        boolean showing = mShowing;
355        boolean occluded = mOccluded;
356        boolean bouncerShowing = mBouncer.isShowing();
357        boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
358
359        if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing)
360                || mFirstUpdate) {
361            if (bouncerDismissible || !showing) {
362                mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
363            } else {
364                mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
365            }
366        }
367        if ((!(showing && !occluded) || bouncerShowing)
368                != (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing) || mFirstUpdate) {
369            if (mPhoneStatusBar.getNavigationBarView() != null) {
370                if (!(showing && !occluded) || bouncerShowing) {
371                    mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
372                            getNavBarShowDelay());
373                } else {
374                    mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
375                    mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
376                }
377            }
378        }
379
380        if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
381            mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
382            mPhoneStatusBar.setBouncerShowing(bouncerShowing);
383            mScrimController.setBouncerShowing(bouncerShowing);
384        }
385
386        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
387        if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
388            updateMonitor.sendKeyguardVisibilityChanged(showing && !occluded);
389        }
390        if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
391            updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
392        }
393
394        mFirstUpdate = false;
395        mLastShowing = showing;
396        mLastOccluded = occluded;
397        mLastBouncerShowing = bouncerShowing;
398        mLastBouncerDismissible = bouncerDismissible;
399
400        mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
401    }
402
403    public boolean onMenuPressed() {
404        return mBouncer.onMenuPressed();
405    }
406
407    public boolean interceptMediaKey(KeyEvent event) {
408        return mBouncer.interceptMediaKey(event);
409    }
410
411    public void onActivityDrawn() {
412        if (mPhoneStatusBar.isCollapsing()) {
413            mPhoneStatusBar.addPostCollapseAction(new Runnable() {
414                @Override
415                public void run() {
416                    mViewMediatorCallback.readyForKeyguardDone();
417                }
418            });
419        } else {
420            mViewMediatorCallback.readyForKeyguardDone();
421        }
422    }
423
424    public boolean shouldDisableWindowAnimationsForUnlock() {
425        return mPhoneStatusBar.isInLaunchTransition();
426    }
427
428    public boolean isGoingToNotificationShade() {
429        return mPhoneStatusBar.isGoingToNotificationShade();
430    }
431
432    public boolean isSecure(int userId) {
433        return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
434    }
435
436    public boolean isInputRestricted() {
437        return mViewMediatorCallback.isInputRestricted();
438    }
439
440    public void keyguardGoingAway() {
441        mPhoneStatusBar.keyguardGoingAway();
442    }
443
444    public void animateCollapsePanels() {
445        mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
446    }
447}
448