StatusBarKeyguardViewManager.java revision 31b844ba0cf418b758fbe45023d0202f208c0191
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;
34
35import static com.android.keyguard.KeyguardHostView.OnDismissAction;
36
37/**
38 * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
39 * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
40 * which is in turn, reported to this class by the current
41 * {@link com.android.keyguard.KeyguardViewBase}.
42 */
43public class StatusBarKeyguardViewManager {
44
45    // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
46    private static final long HIDE_TIMING_CORRECTION_MS = -3 * 16;
47
48    // Delay for showing the navigation bar when the bouncer appears. This should be kept in sync
49    // with the appear animations of the PIN/pattern/password views.
50    private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
51
52    private static String TAG = "StatusBarKeyguardViewManager";
53
54    private final Context mContext;
55
56    private LockPatternUtils mLockPatternUtils;
57    private ViewMediatorCallback mViewMediatorCallback;
58    private PhoneStatusBar mPhoneStatusBar;
59    private ScrimController mScrimController;
60
61    private ViewGroup mContainer;
62    private StatusBarWindowManager mStatusBarWindowManager;
63
64    private boolean mScreenOn = false;
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 OnDismissAction mAfterKeyguardGoneAction;
75
76    public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
77            LockPatternUtils lockPatternUtils) {
78        mContext = context;
79        mViewMediatorCallback = callback;
80        mLockPatternUtils = lockPatternUtils;
81    }
82
83    public void registerStatusBar(PhoneStatusBar phoneStatusBar,
84            ViewGroup container, StatusBarWindowManager statusBarWindowManager,
85            ScrimController scrimController) {
86        mPhoneStatusBar = phoneStatusBar;
87        mContainer = container;
88        mStatusBarWindowManager = statusBarWindowManager;
89        mScrimController = scrimController;
90        mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
91                mStatusBarWindowManager, container);
92    }
93
94    /**
95     * Show the keyguard.  Will handle creating and attaching to the view manager
96     * lazily.
97     */
98    public void show(Bundle options) {
99        mShowing = true;
100        mStatusBarWindowManager.setKeyguardShowing(true);
101        reset();
102    }
103
104    /**
105     * Shows the notification keyguard or the bouncer depending on
106     * {@link KeyguardBouncer#needsFullscreenBouncer()}.
107     */
108    private void showBouncerOrKeyguard() {
109        if (mBouncer.needsFullscreenBouncer()) {
110
111            // The keyguard might be showing (already). So we need to hide it.
112            mPhoneStatusBar.hideKeyguard();
113            mBouncer.show();
114        } else {
115            mPhoneStatusBar.showKeyguard();
116            mBouncer.hide(false /* destroyView */);
117            mBouncer.prepare();
118        }
119    }
120
121    private void showBouncer() {
122        if (mShowing) {
123            mBouncer.show();
124        }
125        updateStates();
126    }
127
128    public void dismissWithAction(OnDismissAction r, boolean afterKeyguardGone) {
129        if (mShowing) {
130            if (!afterKeyguardGone) {
131                mBouncer.showWithDismissAction(r);
132            } else {
133                mBouncer.show();
134                mAfterKeyguardGoneAction = r;
135            }
136        }
137        updateStates();
138    }
139
140    /**
141     * Reset the state of the view.
142     */
143    public void reset() {
144        if (mShowing) {
145            if (mOccluded) {
146                mPhoneStatusBar.hideKeyguard();
147                mBouncer.hide(false /* destroyView */);
148            } else {
149                showBouncerOrKeyguard();
150            }
151            updateStates();
152        }
153    }
154
155    public void onScreenTurnedOff() {
156        mScreenOn = false;
157        mPhoneStatusBar.onScreenTurnedOff();
158        mBouncer.onScreenTurnedOff();
159    }
160
161    public void onScreenTurnedOn(final IKeyguardShowCallback callback) {
162        mScreenOn = true;
163        mPhoneStatusBar.onScreenTurnedOn();
164        if (callback != null) {
165            callbackAfterDraw(callback);
166        }
167    }
168
169    private void callbackAfterDraw(final IKeyguardShowCallback callback) {
170        mContainer.post(new Runnable() {
171            @Override
172            public void run() {
173                try {
174                    callback.onShown(mContainer.getWindowToken());
175                } catch (RemoteException e) {
176                    Slog.w(TAG, "Exception calling onShown():", e);
177                }
178            }
179        });
180    }
181
182    public void verifyUnlock() {
183        dismiss();
184    }
185
186    public void setNeedsInput(boolean needsInput) {
187        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
188    }
189
190    public void updateUserActivityTimeout() {
191        mStatusBarWindowManager.setKeyguardUserActivityTimeout(mBouncer.getUserActivityTimeout());
192    }
193
194    public void setOccluded(boolean occluded) {
195        if (occluded && !mOccluded && mShowing) {
196            if (mPhoneStatusBar.isInLaunchTransition()) {
197                mOccluded = true;
198                mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
199                        new Runnable() {
200                            @Override
201                            public void run() {
202                                mStatusBarWindowManager.setKeyguardOccluded(true);
203                                reset();
204                            }
205                        });
206                return;
207            }
208        }
209        mOccluded = occluded;
210        mStatusBarWindowManager.setKeyguardOccluded(occluded);
211        reset();
212    }
213
214    public boolean isOccluded() {
215        return mOccluded;
216    }
217
218    /**
219     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
220     * security view of the bouncer.
221     *
222     * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
223     *                       no action should be run
224     */
225    public void startPreHideAnimation(Runnable finishRunnable) {
226        if (mBouncer.isShowing()) {
227            mBouncer.startPreHideAnimation(finishRunnable);
228        } else if (finishRunnable != null) {
229            finishRunnable.run();
230        }
231    }
232
233    /**
234     * Hides the keyguard view
235     */
236    public void hide(long startTime, final long fadeoutDuration) {
237        mShowing = false;
238
239        long uptimeMillis = SystemClock.uptimeMillis();
240        long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
241
242        if (mPhoneStatusBar.isInLaunchTransition() ) {
243            mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
244                @Override
245                public void run() {
246                    mStatusBarWindowManager.setKeyguardShowing(false);
247                    mStatusBarWindowManager.setKeyguardFadingAway(true);
248                    mBouncer.hide(true /* destroyView */);
249                    updateStates();
250                    mScrimController.animateKeyguardFadingOut(
251                            PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
252                            PhoneStatusBar.FADE_KEYGUARD_DURATION, null);
253                }
254            }, new Runnable() {
255                @Override
256                public void run() {
257                    mPhoneStatusBar.hideKeyguard();
258                    mStatusBarWindowManager.setKeyguardFadingAway(false);
259                    mViewMediatorCallback.keyguardGone();
260                    executeAfterKeyguardGoneAction();
261                }
262            });
263        } else {
264            mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
265            boolean staying = mPhoneStatusBar.hideKeyguard();
266            if (!staying) {
267                mStatusBarWindowManager.setKeyguardFadingAway(true);
268                mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
269                    @Override
270                    public void run() {
271                        mStatusBarWindowManager.setKeyguardFadingAway(false);
272                        mPhoneStatusBar.finishKeyguardFadingAway();
273                        WindowManagerGlobal.getInstance().trimMemory(
274                                ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
275                    }
276                });
277            } else {
278                mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
279                mPhoneStatusBar.finishKeyguardFadingAway();
280            }
281            mStatusBarWindowManager.setKeyguardShowing(false);
282            mBouncer.hide(true /* destroyView */);
283            mViewMediatorCallback.keyguardGone();
284            executeAfterKeyguardGoneAction();
285            updateStates();
286        }
287
288    }
289
290    private void executeAfterKeyguardGoneAction() {
291        if (mAfterKeyguardGoneAction != null) {
292            mAfterKeyguardGoneAction.onDismiss();
293            mAfterKeyguardGoneAction = null;
294        }
295    }
296
297    /**
298     * Dismisses the keyguard by going to the next screen or making it gone.
299     */
300    public void dismiss() {
301        if (mScreenOn) {
302            showBouncer();
303        }
304    }
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.needsFullscreenBouncer();
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
401    public boolean onMenuPressed() {
402        return mBouncer.onMenuPressed();
403    }
404
405    public boolean interceptMediaKey(KeyEvent event) {
406        return mBouncer.interceptMediaKey(event);
407    }
408
409    public void onActivityDrawn() {
410        if (mPhoneStatusBar.isCollapsing()) {
411            mPhoneStatusBar.addPostCollapseAction(new Runnable() {
412                @Override
413                public void run() {
414                    mViewMediatorCallback.readyForKeyguardDone();
415                }
416            });
417        } else {
418            mViewMediatorCallback.readyForKeyguardDone();
419        }
420    }
421
422    public boolean shouldDisableWindowAnimationsForUnlock() {
423        return mPhoneStatusBar.isInLaunchTransition();
424    }
425
426    public boolean isGoingToNotificationShade() {
427        return mPhoneStatusBar.isGoingToNotificationShade();
428    }
429
430    public boolean isSecure(int userId) {
431        return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
432    }
433}
434