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.ViewRootImpl;
     28 import android.view.WindowManagerGlobal;
     29 
     30 import com.android.internal.widget.LockPatternUtils;
     31 import com.android.keyguard.KeyguardUpdateMonitor;
     32 import com.android.keyguard.ViewMediatorCallback;
     33 import com.android.systemui.SystemUIFactory;
     34 import com.android.systemui.statusbar.CommandQueue;
     35 import com.android.systemui.statusbar.RemoteInputController;
     36 
     37 import 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  */
     45 public 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     // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to
     57     // make everything a bit slower to bridge a gap until the user is unlocked and home screen has
     58     // dranw its first frame.
     59     private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
     60 
     61     private static String TAG = "StatusBarKeyguardViewManager";
     62 
     63     protected final Context mContext;
     64 
     65     protected LockPatternUtils mLockPatternUtils;
     66     protected ViewMediatorCallback mViewMediatorCallback;
     67     protected PhoneStatusBar mPhoneStatusBar;
     68     private ScrimController mScrimController;
     69     private FingerprintUnlockController mFingerprintUnlockController;
     70 
     71     private ViewGroup mContainer;
     72     private StatusBarWindowManager mStatusBarWindowManager;
     73 
     74     private boolean mDeviceInteractive = false;
     75     private boolean mScreenTurnedOn;
     76     protected KeyguardBouncer mBouncer;
     77     protected boolean mShowing;
     78     protected boolean mOccluded;
     79     protected boolean mRemoteInputActive;
     80 
     81     protected boolean mFirstUpdate = true;
     82     protected boolean mLastShowing;
     83     protected boolean mLastOccluded;
     84     private boolean mLastBouncerShowing;
     85     private boolean mLastBouncerDismissible;
     86     protected boolean mLastRemoteInputActive;
     87 
     88     private OnDismissAction mAfterKeyguardGoneAction;
     89     private boolean mDeviceWillWakeUp;
     90     private boolean mDeferScrimFadeOut;
     91 
     92     public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
     93             LockPatternUtils lockPatternUtils) {
     94         mContext = context;
     95         mViewMediatorCallback = callback;
     96         mLockPatternUtils = lockPatternUtils;
     97     }
     98 
     99     public void registerStatusBar(PhoneStatusBar phoneStatusBar,
    100             ViewGroup container, StatusBarWindowManager statusBarWindowManager,
    101             ScrimController scrimController,
    102             FingerprintUnlockController fingerprintUnlockController) {
    103         mPhoneStatusBar = phoneStatusBar;
    104         mContainer = container;
    105         mStatusBarWindowManager = statusBarWindowManager;
    106         mScrimController = scrimController;
    107         mFingerprintUnlockController = fingerprintUnlockController;
    108         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
    109                 mViewMediatorCallback, mLockPatternUtils, mStatusBarWindowManager, container);
    110     }
    111 
    112     /**
    113      * Show the keyguard.  Will handle creating and attaching to the view manager
    114      * lazily.
    115      */
    116     public void show(Bundle options) {
    117         mShowing = true;
    118         mStatusBarWindowManager.setKeyguardShowing(true);
    119         mScrimController.abortKeyguardFadingOut();
    120         reset();
    121     }
    122 
    123     /**
    124      * Shows the notification keyguard or the bouncer depending on
    125      * {@link KeyguardBouncer#needsFullscreenBouncer()}.
    126      */
    127     protected void showBouncerOrKeyguard() {
    128         if (mBouncer.needsFullscreenBouncer()) {
    129 
    130             // The keyguard might be showing (already). So we need to hide it.
    131             mPhoneStatusBar.hideKeyguard();
    132             mBouncer.show(true /* resetSecuritySelection */);
    133         } else {
    134             mPhoneStatusBar.showKeyguard();
    135             mBouncer.hide(false /* destroyView */);
    136             mBouncer.prepare();
    137         }
    138     }
    139 
    140     private void showBouncer() {
    141         if (mShowing) {
    142             mBouncer.show(false /* resetSecuritySelection */);
    143         }
    144         updateStates();
    145     }
    146 
    147     public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
    148             boolean afterKeyguardGone) {
    149         if (mShowing) {
    150             if (!afterKeyguardGone) {
    151                 mBouncer.showWithDismissAction(r, cancelAction);
    152             } else {
    153                 mBouncer.show(false /* resetSecuritySelection */);
    154                 mAfterKeyguardGoneAction = r;
    155             }
    156         }
    157         updateStates();
    158     }
    159 
    160     /**
    161      * Reset the state of the view.
    162      */
    163     public void reset() {
    164         if (mShowing) {
    165             if (mOccluded) {
    166                 mPhoneStatusBar.hideKeyguard();
    167                 mPhoneStatusBar.stopWaitingForKeyguardExit();
    168                 mBouncer.hide(false /* destroyView */);
    169             } else {
    170                 showBouncerOrKeyguard();
    171             }
    172             KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
    173             updateStates();
    174         }
    175     }
    176 
    177     public void onStartedGoingToSleep() {
    178         mPhoneStatusBar.onStartedGoingToSleep();
    179     }
    180 
    181     public void onFinishedGoingToSleep() {
    182         mDeviceInteractive = false;
    183         mPhoneStatusBar.onFinishedGoingToSleep();
    184         mBouncer.onScreenTurnedOff();
    185     }
    186 
    187     public void onStartedWakingUp() {
    188         Trace.beginSection("StatusBarKeyguardViewManager#onStartedWakingUp");
    189         mDeviceInteractive = true;
    190         mDeviceWillWakeUp = false;
    191         mPhoneStatusBar.onStartedWakingUp();
    192         Trace.endSection();
    193     }
    194 
    195     public void onScreenTurningOn() {
    196         Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurningOn");
    197         mPhoneStatusBar.onScreenTurningOn();
    198         Trace.endSection();
    199     }
    200 
    201     public boolean isScreenTurnedOn() {
    202         return mScreenTurnedOn;
    203     }
    204 
    205     public void onScreenTurnedOn() {
    206         Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurnedOn");
    207         mScreenTurnedOn = true;
    208         if (mDeferScrimFadeOut) {
    209             mDeferScrimFadeOut = false;
    210             animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
    211                     true /* skipFirstFrame */);
    212             updateStates();
    213         }
    214         mPhoneStatusBar.onScreenTurnedOn();
    215         Trace.endSection();
    216     }
    217 
    218     @Override
    219     public void onRemoteInputActive(boolean active) {
    220         mRemoteInputActive = active;
    221         updateStates();
    222     }
    223 
    224     public void onScreenTurnedOff() {
    225         mScreenTurnedOn = false;
    226         mPhoneStatusBar.onScreenTurnedOff();
    227     }
    228 
    229     public void notifyDeviceWakeUpRequested() {
    230         mDeviceWillWakeUp = !mDeviceInteractive;
    231     }
    232 
    233     public void verifyUnlock() {
    234         dismiss();
    235     }
    236 
    237     public void setNeedsInput(boolean needsInput) {
    238         mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
    239     }
    240 
    241     public boolean isUnlockWithWallpaper() {
    242         return mStatusBarWindowManager.isShowingWallpaper();
    243     }
    244 
    245     public void setOccluded(boolean occluded, boolean animate) {
    246         if (occluded && !mOccluded && mShowing) {
    247             if (mPhoneStatusBar.isInLaunchTransition()) {
    248                 mOccluded = true;
    249                 mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
    250                         new Runnable() {
    251                             @Override
    252                             public void run() {
    253                                 mStatusBarWindowManager.setKeyguardOccluded(mOccluded);
    254                                 reset();
    255                             }
    256                         });
    257                 return;
    258             }
    259         }
    260         mOccluded = occluded;
    261         mPhoneStatusBar.updateMediaMetaData(false, animate && !occluded);
    262         mStatusBarWindowManager.setKeyguardOccluded(occluded);
    263         reset();
    264         if (animate && !occluded) {
    265             mPhoneStatusBar.animateKeyguardUnoccluding();
    266         }
    267     }
    268 
    269     public boolean isOccluded() {
    270         return mOccluded;
    271     }
    272 
    273     /**
    274      * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
    275      * security view of the bouncer.
    276      *
    277      * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
    278      *                       no action should be run
    279      */
    280     public void startPreHideAnimation(Runnable finishRunnable) {
    281         if (mBouncer.isShowing()) {
    282             mBouncer.startPreHideAnimation(finishRunnable);
    283         } else if (finishRunnable != null) {
    284             finishRunnable.run();
    285         }
    286     }
    287 
    288     /**
    289      * Hides the keyguard view
    290      */
    291     public void hide(long startTime, long fadeoutDuration) {
    292         mShowing = false;
    293 
    294         if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
    295             fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
    296         }
    297         long uptimeMillis = SystemClock.uptimeMillis();
    298         long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
    299 
    300         if (mPhoneStatusBar.isInLaunchTransition() ) {
    301             mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
    302                 @Override
    303                 public void run() {
    304                     mStatusBarWindowManager.setKeyguardShowing(false);
    305                     mStatusBarWindowManager.setKeyguardFadingAway(true);
    306                     mBouncer.hide(true /* destroyView */);
    307                     updateStates();
    308                     mScrimController.animateKeyguardFadingOut(
    309                             PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
    310                             PhoneStatusBar.FADE_KEYGUARD_DURATION, null,
    311                             false /* skipFirstFrame */);
    312                 }
    313             }, new Runnable() {
    314                 @Override
    315                 public void run() {
    316                     mPhoneStatusBar.hideKeyguard();
    317                     mStatusBarWindowManager.setKeyguardFadingAway(false);
    318                     mViewMediatorCallback.keyguardGone();
    319                     executeAfterKeyguardGoneAction();
    320                 }
    321             });
    322         } else {
    323             if (mFingerprintUnlockController.getMode()
    324                     == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
    325                 mFingerprintUnlockController.startKeyguardFadingAway();
    326                 mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240);
    327                 mStatusBarWindowManager.setKeyguardFadingAway(true);
    328                 mPhoneStatusBar.fadeKeyguardWhilePulsing();
    329                 animateScrimControllerKeyguardFadingOut(0, 240, new Runnable() {
    330                     @Override
    331                     public void run() {
    332                         mPhoneStatusBar.hideKeyguard();
    333                     }
    334                 }, false /* skipFirstFrame */);
    335             } else {
    336                 mFingerprintUnlockController.startKeyguardFadingAway();
    337                 mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
    338                 boolean staying = mPhoneStatusBar.hideKeyguard();
    339                 if (!staying) {
    340                     mStatusBarWindowManager.setKeyguardFadingAway(true);
    341                     if (mFingerprintUnlockController.getMode()
    342                             == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
    343                         if (!mScreenTurnedOn) {
    344                             mDeferScrimFadeOut = true;
    345                         } else {
    346 
    347                             // Screen is already on, don't defer with fading out.
    348                             animateScrimControllerKeyguardFadingOut(0,
    349                                     WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
    350                                     true /* skipFirstFrame */);
    351                         }
    352                     } else {
    353                         animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
    354                                 false /* skipFirstFrame */);
    355                     }
    356                 } else {
    357                     mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
    358                     mPhoneStatusBar.finishKeyguardFadingAway();
    359                 }
    360             }
    361             mStatusBarWindowManager.setKeyguardShowing(false);
    362             mBouncer.hide(true /* destroyView */);
    363             mViewMediatorCallback.keyguardGone();
    364             executeAfterKeyguardGoneAction();
    365             updateStates();
    366         }
    367     }
    368 
    369     public void onDensityOrFontScaleChanged() {
    370         mBouncer.hide(true /* destroyView */);
    371     }
    372 
    373     private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
    374             boolean skipFirstFrame) {
    375         animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
    376                 skipFirstFrame);
    377     }
    378 
    379     private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
    380             final Runnable endRunnable, boolean skipFirstFrame) {
    381         Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0);
    382         mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() {
    383             @Override
    384             public void run() {
    385                 if (endRunnable != null) {
    386                     endRunnable.run();
    387                 }
    388                 mStatusBarWindowManager.setKeyguardFadingAway(false);
    389                 mPhoneStatusBar.finishKeyguardFadingAway();
    390                 mFingerprintUnlockController.finishKeyguardFadingAway();
    391                 WindowManagerGlobal.getInstance().trimMemory(
    392                         ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
    393                 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
    394             }
    395         }, skipFirstFrame);
    396     }
    397 
    398     private void executeAfterKeyguardGoneAction() {
    399         if (mAfterKeyguardGoneAction != null) {
    400             mAfterKeyguardGoneAction.onDismiss();
    401             mAfterKeyguardGoneAction = null;
    402         }
    403     }
    404 
    405     /**
    406      * Dismisses the keyguard by going to the next screen or making it gone.
    407      */
    408     public void dismiss() {
    409         if (mDeviceInteractive || mDeviceWillWakeUp) {
    410             showBouncer();
    411         }
    412     }
    413 
    414     /**
    415      * WARNING: This method might cause Binder calls.
    416      */
    417     public boolean isSecure() {
    418         return mBouncer.isSecure();
    419     }
    420 
    421     /**
    422      * @return Whether the keyguard is showing
    423      */
    424     public boolean isShowing() {
    425         return mShowing;
    426     }
    427 
    428     /**
    429      * Notifies this manager that the back button has been pressed.
    430      *
    431      * @return whether the back press has been handled
    432      */
    433     public boolean onBackPressed() {
    434         if (mBouncer.isShowing()) {
    435             mPhoneStatusBar.endAffordanceLaunch();
    436             reset();
    437             return true;
    438         }
    439         return false;
    440     }
    441 
    442     public boolean isBouncerShowing() {
    443         return mBouncer.isShowing();
    444     }
    445 
    446     private long getNavBarShowDelay() {
    447         if (mPhoneStatusBar.isKeyguardFadingAway()) {
    448             return mPhoneStatusBar.getKeyguardFadingAwayDelay();
    449         } else {
    450 
    451             // Keyguard is not going away, thus we are showing the navigation bar because the
    452             // bouncer is appearing.
    453             return NAV_BAR_SHOW_DELAY_BOUNCER;
    454         }
    455     }
    456 
    457     private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
    458         @Override
    459         public void run() {
    460             mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE);
    461         }
    462     };
    463 
    464     protected void updateStates() {
    465         int vis = mContainer.getSystemUiVisibility();
    466         boolean showing = mShowing;
    467         boolean occluded = mOccluded;
    468         boolean bouncerShowing = mBouncer.isShowing();
    469         boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
    470         boolean remoteInputActive = mRemoteInputActive;
    471 
    472         if ((bouncerDismissible || !showing || remoteInputActive) !=
    473                 (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
    474                 || mFirstUpdate) {
    475             if (bouncerDismissible || !showing || remoteInputActive) {
    476                 mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
    477             } else {
    478                 mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
    479             }
    480         }
    481 
    482         boolean navBarVisible = isNavBarVisible();
    483         boolean lastNavBarVisible = getLastNavBarVisible();
    484         if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
    485             if (mPhoneStatusBar.getNavigationBarView() != null) {
    486                 if (navBarVisible) {
    487                     long delay = getNavBarShowDelay();
    488                     if (delay == 0) {
    489                         mMakeNavigationBarVisibleRunnable.run();
    490                     } else {
    491                         mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
    492                                 delay);
    493                     }
    494                 } else {
    495                     mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
    496                     mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
    497                 }
    498             }
    499         }
    500 
    501         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
    502             mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
    503             mPhoneStatusBar.setBouncerShowing(bouncerShowing);
    504             mScrimController.setBouncerShowing(bouncerShowing);
    505         }
    506 
    507         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
    508         if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
    509             updateMonitor.onKeyguardVisibilityChanged(showing && !occluded);
    510         }
    511         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
    512             updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
    513         }
    514 
    515         mFirstUpdate = false;
    516         mLastShowing = showing;
    517         mLastOccluded = occluded;
    518         mLastBouncerShowing = bouncerShowing;
    519         mLastBouncerDismissible = bouncerDismissible;
    520         mLastRemoteInputActive = remoteInputActive;
    521 
    522         mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
    523     }
    524 
    525     /**
    526      * @return Whether the navigation bar should be made visible based on the current state.
    527      */
    528     protected boolean isNavBarVisible() {
    529         return !(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive;
    530     }
    531 
    532     /**
    533      * @return Whether the navigation bar was made visible based on the last known state.
    534      */
    535     protected boolean getLastNavBarVisible() {
    536         return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive;
    537     }
    538 
    539     public boolean shouldDismissOnMenuPressed() {
    540         return mBouncer.shouldDismissOnMenuPressed();
    541     }
    542 
    543     public boolean interceptMediaKey(KeyEvent event) {
    544         return mBouncer.interceptMediaKey(event);
    545     }
    546 
    547     public void onActivityDrawn() {
    548         if (mPhoneStatusBar.isCollapsing()) {
    549             mPhoneStatusBar.addPostCollapseAction(new Runnable() {
    550                 @Override
    551                 public void run() {
    552                     mViewMediatorCallback.readyForKeyguardDone();
    553                 }
    554             });
    555         } else {
    556             mViewMediatorCallback.readyForKeyguardDone();
    557         }
    558     }
    559 
    560     public boolean shouldDisableWindowAnimationsForUnlock() {
    561         return mPhoneStatusBar.isInLaunchTransition();
    562     }
    563 
    564     public boolean isGoingToNotificationShade() {
    565         return mPhoneStatusBar.isGoingToNotificationShade();
    566     }
    567 
    568     public boolean isSecure(int userId) {
    569         return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
    570     }
    571 
    572     public boolean isInputRestricted() {
    573         return mViewMediatorCallback.isInputRestricted();
    574     }
    575 
    576     public void keyguardGoingAway() {
    577         mPhoneStatusBar.keyguardGoingAway();
    578     }
    579 
    580     public void animateCollapsePanels(float speedUpFactor) {
    581         mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
    582                 false /* delayed */, speedUpFactor);
    583     }
    584 
    585     /**
    586      * Notifies that the user has authenticated by other means than using the bouncer, for example,
    587      * fingerprint.
    588      */
    589     public void notifyKeyguardAuthenticated(boolean strongAuth) {
    590         mBouncer.notifyKeyguardAuthenticated(strongAuth);
    591     }
    592 
    593     public void showBouncerMessage(String message, int color) {
    594         mBouncer.showMessage(message, color);
    595     }
    596 
    597     public ViewRootImpl getViewRootImpl() {
    598         return mPhoneStatusBar.getStatusBarView().getViewRootImpl();
    599     }
    600 }
    601