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) {
    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, false);
    262         mStatusBarWindowManager.setKeyguardOccluded(occluded);
    263         reset();
    264     }
    265 
    266     public boolean isOccluded() {
    267         return mOccluded;
    268     }
    269 
    270     /**
    271      * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
    272      * security view of the bouncer.
    273      *
    274      * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
    275      *                       no action should be run
    276      */
    277     public void startPreHideAnimation(Runnable finishRunnable) {
    278         if (mBouncer.isShowing()) {
    279             mBouncer.startPreHideAnimation(finishRunnable);
    280         } else if (finishRunnable != null) {
    281             finishRunnable.run();
    282         }
    283     }
    284 
    285     /**
    286      * Hides the keyguard view
    287      */
    288     public void hide(long startTime, long fadeoutDuration) {
    289         mShowing = false;
    290 
    291         if (!KeyguardUpdateMonitor.getInstance(mContext).isUserUnlocked()) {
    292             fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
    293         }
    294         long uptimeMillis = SystemClock.uptimeMillis();
    295         long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
    296 
    297         if (mPhoneStatusBar.isInLaunchTransition() ) {
    298             mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
    299                 @Override
    300                 public void run() {
    301                     mStatusBarWindowManager.setKeyguardShowing(false);
    302                     mStatusBarWindowManager.setKeyguardFadingAway(true);
    303                     mBouncer.hide(true /* destroyView */);
    304                     updateStates();
    305                     mScrimController.animateKeyguardFadingOut(
    306                             PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
    307                             PhoneStatusBar.FADE_KEYGUARD_DURATION, null,
    308                             false /* skipFirstFrame */);
    309                 }
    310             }, new Runnable() {
    311                 @Override
    312                 public void run() {
    313                     mPhoneStatusBar.hideKeyguard();
    314                     mStatusBarWindowManager.setKeyguardFadingAway(false);
    315                     mViewMediatorCallback.keyguardGone();
    316                     executeAfterKeyguardGoneAction();
    317                 }
    318             });
    319         } else {
    320             if (mFingerprintUnlockController.getMode()
    321                     == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
    322                 mFingerprintUnlockController.startKeyguardFadingAway();
    323                 mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240);
    324                 mStatusBarWindowManager.setKeyguardFadingAway(true);
    325                 mPhoneStatusBar.fadeKeyguardWhilePulsing();
    326                 animateScrimControllerKeyguardFadingOut(0, 240, new Runnable() {
    327                     @Override
    328                     public void run() {
    329                         mPhoneStatusBar.hideKeyguard();
    330                     }
    331                 }, false /* skipFirstFrame */);
    332             } else {
    333                 mFingerprintUnlockController.startKeyguardFadingAway();
    334                 mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
    335                 boolean staying = mPhoneStatusBar.hideKeyguard();
    336                 if (!staying) {
    337                     mStatusBarWindowManager.setKeyguardFadingAway(true);
    338                     if (mFingerprintUnlockController.getMode()
    339                             == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
    340                         if (!mScreenTurnedOn) {
    341                             mDeferScrimFadeOut = true;
    342                         } else {
    343 
    344                             // Screen is already on, don't defer with fading out.
    345                             animateScrimControllerKeyguardFadingOut(0,
    346                                     WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
    347                                     true /* skipFirstFrame */);
    348                         }
    349                     } else {
    350                         animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
    351                                 false /* skipFirstFrame */);
    352                     }
    353                 } else {
    354                     mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
    355                     mPhoneStatusBar.finishKeyguardFadingAway();
    356                 }
    357             }
    358             mStatusBarWindowManager.setKeyguardShowing(false);
    359             mBouncer.hide(true /* destroyView */);
    360             mViewMediatorCallback.keyguardGone();
    361             executeAfterKeyguardGoneAction();
    362             updateStates();
    363         }
    364     }
    365 
    366     public void onDensityOrFontScaleChanged() {
    367         mBouncer.hide(true /* destroyView */);
    368     }
    369 
    370     private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
    371             boolean skipFirstFrame) {
    372         animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
    373                 skipFirstFrame);
    374     }
    375 
    376     private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
    377             final Runnable endRunnable, boolean skipFirstFrame) {
    378         Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0);
    379         mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() {
    380             @Override
    381             public void run() {
    382                 if (endRunnable != null) {
    383                     endRunnable.run();
    384                 }
    385                 mStatusBarWindowManager.setKeyguardFadingAway(false);
    386                 mPhoneStatusBar.finishKeyguardFadingAway();
    387                 mFingerprintUnlockController.finishKeyguardFadingAway();
    388                 WindowManagerGlobal.getInstance().trimMemory(
    389                         ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
    390                 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
    391             }
    392         }, skipFirstFrame);
    393     }
    394 
    395     private void executeAfterKeyguardGoneAction() {
    396         if (mAfterKeyguardGoneAction != null) {
    397             mAfterKeyguardGoneAction.onDismiss();
    398             mAfterKeyguardGoneAction = null;
    399         }
    400     }
    401 
    402     /**
    403      * Dismisses the keyguard by going to the next screen or making it gone.
    404      */
    405     public void dismiss() {
    406         if (mDeviceInteractive || mDeviceWillWakeUp) {
    407             showBouncer();
    408         }
    409     }
    410 
    411     /**
    412      * WARNING: This method might cause Binder calls.
    413      */
    414     public boolean isSecure() {
    415         return mBouncer.isSecure();
    416     }
    417 
    418     /**
    419      * @return Whether the keyguard is showing
    420      */
    421     public boolean isShowing() {
    422         return mShowing;
    423     }
    424 
    425     /**
    426      * Notifies this manager that the back button has been pressed.
    427      *
    428      * @return whether the back press has been handled
    429      */
    430     public boolean onBackPressed() {
    431         if (mBouncer.isShowing()) {
    432             mPhoneStatusBar.endAffordanceLaunch();
    433             reset();
    434             return true;
    435         }
    436         return false;
    437     }
    438 
    439     public boolean isBouncerShowing() {
    440         return mBouncer.isShowing();
    441     }
    442 
    443     private long getNavBarShowDelay() {
    444         if (mPhoneStatusBar.isKeyguardFadingAway()) {
    445             return mPhoneStatusBar.getKeyguardFadingAwayDelay();
    446         } else {
    447 
    448             // Keyguard is not going away, thus we are showing the navigation bar because the
    449             // bouncer is appearing.
    450             return NAV_BAR_SHOW_DELAY_BOUNCER;
    451         }
    452     }
    453 
    454     private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
    455         @Override
    456         public void run() {
    457             mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE);
    458         }
    459     };
    460 
    461     protected void updateStates() {
    462         int vis = mContainer.getSystemUiVisibility();
    463         boolean showing = mShowing;
    464         boolean occluded = mOccluded;
    465         boolean bouncerShowing = mBouncer.isShowing();
    466         boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
    467         boolean remoteInputActive = mRemoteInputActive;
    468 
    469         if ((bouncerDismissible || !showing || remoteInputActive) !=
    470                 (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
    471                 || mFirstUpdate) {
    472             if (bouncerDismissible || !showing || remoteInputActive) {
    473                 mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
    474             } else {
    475                 mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
    476             }
    477         }
    478 
    479         boolean navBarVisible = isNavBarVisible();
    480         boolean lastNavBarVisible = getLastNavBarVisible();
    481         if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
    482             if (mPhoneStatusBar.getNavigationBarView() != null) {
    483                 if (navBarVisible) {
    484                     long delay = getNavBarShowDelay();
    485                     if (delay == 0) {
    486                         mMakeNavigationBarVisibleRunnable.run();
    487                     } else {
    488                         mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
    489                                 delay);
    490                     }
    491                 } else {
    492                     mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
    493                     mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
    494                 }
    495             }
    496         }
    497 
    498         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
    499             mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
    500             mPhoneStatusBar.setBouncerShowing(bouncerShowing);
    501             mScrimController.setBouncerShowing(bouncerShowing);
    502         }
    503 
    504         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
    505         if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
    506             updateMonitor.onKeyguardVisibilityChanged(showing && !occluded);
    507         }
    508         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
    509             updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
    510         }
    511 
    512         mFirstUpdate = false;
    513         mLastShowing = showing;
    514         mLastOccluded = occluded;
    515         mLastBouncerShowing = bouncerShowing;
    516         mLastBouncerDismissible = bouncerDismissible;
    517         mLastRemoteInputActive = remoteInputActive;
    518 
    519         mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
    520     }
    521 
    522     /**
    523      * @return Whether the navigation bar should be made visible based on the current state.
    524      */
    525     protected boolean isNavBarVisible() {
    526         return !(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive;
    527     }
    528 
    529     /**
    530      * @return Whether the navigation bar was made visible based on the last known state.
    531      */
    532     protected boolean getLastNavBarVisible() {
    533         return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive;
    534     }
    535 
    536     public boolean shouldDismissOnMenuPressed() {
    537         return mBouncer.shouldDismissOnMenuPressed();
    538     }
    539 
    540     public boolean interceptMediaKey(KeyEvent event) {
    541         return mBouncer.interceptMediaKey(event);
    542     }
    543 
    544     public void onActivityDrawn() {
    545         if (mPhoneStatusBar.isCollapsing()) {
    546             mPhoneStatusBar.addPostCollapseAction(new Runnable() {
    547                 @Override
    548                 public void run() {
    549                     mViewMediatorCallback.readyForKeyguardDone();
    550                 }
    551             });
    552         } else {
    553             mViewMediatorCallback.readyForKeyguardDone();
    554         }
    555     }
    556 
    557     public boolean shouldDisableWindowAnimationsForUnlock() {
    558         return mPhoneStatusBar.isInLaunchTransition();
    559     }
    560 
    561     public boolean isGoingToNotificationShade() {
    562         return mPhoneStatusBar.isGoingToNotificationShade();
    563     }
    564 
    565     public boolean isSecure(int userId) {
    566         return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
    567     }
    568 
    569     public boolean isInputRestricted() {
    570         return mViewMediatorCallback.isInputRestricted();
    571     }
    572 
    573     public void keyguardGoingAway() {
    574         mPhoneStatusBar.keyguardGoingAway();
    575     }
    576 
    577     public void animateCollapsePanels(float speedUpFactor) {
    578         mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
    579                 false /* delayed */, speedUpFactor);
    580     }
    581 
    582     /**
    583      * Notifies that the user has authenticated by other means than using the bouncer, for example,
    584      * fingerprint.
    585      */
    586     public void notifyKeyguardAuthenticated(boolean strongAuth) {
    587         mBouncer.notifyKeyguardAuthenticated(strongAuth);
    588     }
    589 
    590     public void showBouncerMessage(String message, int color) {
    591         mBouncer.showMessage(message, color);
    592     }
    593 
    594     public ViewRootImpl getViewRootImpl() {
    595         return mPhoneStatusBar.getStatusBarView().getViewRootImpl();
    596     }
    597 }
    598