Home | History | Annotate | Download | only in phone
      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 
     17 package com.android.systemui.statusbar.phone;
     18 
     19 import android.content.ComponentCallbacks2;
     20 import android.content.Context;
     21 import android.os.Bundle;
     22 import android.os.SystemClock;
     23 import android.os.Trace;
     24 import android.view.KeyEvent;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.view.WindowManagerGlobal;
     28 
     29 import com.android.internal.widget.LockPatternUtils;
     30 import com.android.keyguard.KeyguardUpdateMonitor;
     31 import com.android.keyguard.ViewMediatorCallback;
     32 import com.android.systemui.statusbar.CommandQueue;
     33 
     34 import 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  */
     42 public 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