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.RemoteException;
     23 import android.os.SystemClock;
     24 import android.util.Slog;
     25 import android.view.KeyEvent;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.view.WindowManagerGlobal;
     29 
     30 import com.android.internal.policy.IKeyguardShowCallback;
     31 import com.android.internal.widget.LockPatternUtils;
     32 import com.android.keyguard.KeyguardUpdateMonitor;
     33 import com.android.keyguard.ViewMediatorCallback;
     34 
     35 import 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  */
     43 public 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(true /* resetSecuritySelection */);
    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(false /* resetSecuritySelection */);
    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(false /* resetSecuritySelection */);
    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(mOccluded);
    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     /**
    307      * WARNING: This method might cause Binder calls.
    308      */
    309     public boolean isSecure() {
    310         return mBouncer.isSecure();
    311     }
    312 
    313     /**
    314      * @return Whether the keyguard is showing
    315      */
    316     public boolean isShowing() {
    317         return mShowing;
    318     }
    319 
    320     /**
    321      * Notifies this manager that the back button has been pressed.
    322      *
    323      * @return whether the back press has been handled
    324      */
    325     public boolean onBackPressed() {
    326         if (mBouncer.isShowing()) {
    327             reset();
    328             return true;
    329         }
    330         return false;
    331     }
    332 
    333     public boolean isBouncerShowing() {
    334         return mBouncer.isShowing();
    335     }
    336 
    337     private long getNavBarShowDelay() {
    338         if (mPhoneStatusBar.isKeyguardFadingAway()) {
    339             return mPhoneStatusBar.getKeyguardFadingAwayDelay();
    340         } else {
    341 
    342             // Keyguard is not going away, thus we are showing the navigation bar because the
    343             // bouncer is appearing.
    344             return NAV_BAR_SHOW_DELAY_BOUNCER;
    345         }
    346     }
    347 
    348     private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
    349         @Override
    350         public void run() {
    351             mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE);
    352         }
    353     };
    354 
    355     private void updateStates() {
    356         int vis = mContainer.getSystemUiVisibility();
    357         boolean showing = mShowing;
    358         boolean occluded = mOccluded;
    359         boolean bouncerShowing = mBouncer.isShowing();
    360         boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
    361 
    362         if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing)
    363                 || mFirstUpdate) {
    364             if (bouncerDismissible || !showing) {
    365                 mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
    366             } else {
    367                 mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
    368             }
    369         }
    370         if ((!(showing && !occluded) || bouncerShowing)
    371                 != (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing) || mFirstUpdate) {
    372             if (mPhoneStatusBar.getNavigationBarView() != null) {
    373                 if (!(showing && !occluded) || bouncerShowing) {
    374                     mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
    375                             getNavBarShowDelay());
    376                 } else {
    377                     mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
    378                     mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
    379                 }
    380             }
    381         }
    382 
    383         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
    384             mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
    385             mPhoneStatusBar.setBouncerShowing(bouncerShowing);
    386             mScrimController.setBouncerShowing(bouncerShowing);
    387         }
    388 
    389         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
    390         if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
    391             updateMonitor.sendKeyguardVisibilityChanged(showing && !occluded);
    392         }
    393         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
    394             updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
    395         }
    396 
    397         mFirstUpdate = false;
    398         mLastShowing = showing;
    399         mLastOccluded = occluded;
    400         mLastBouncerShowing = bouncerShowing;
    401         mLastBouncerDismissible = bouncerDismissible;
    402 
    403         mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
    404     }
    405 
    406     public boolean onMenuPressed() {
    407         return mBouncer.onMenuPressed();
    408     }
    409 
    410     public boolean interceptMediaKey(KeyEvent event) {
    411         return mBouncer.interceptMediaKey(event);
    412     }
    413 
    414     public void onActivityDrawn() {
    415         if (mPhoneStatusBar.isCollapsing()) {
    416             mPhoneStatusBar.addPostCollapseAction(new Runnable() {
    417                 @Override
    418                 public void run() {
    419                     mViewMediatorCallback.readyForKeyguardDone();
    420                 }
    421             });
    422         } else {
    423             mViewMediatorCallback.readyForKeyguardDone();
    424         }
    425     }
    426 
    427     public boolean shouldDisableWindowAnimationsForUnlock() {
    428         return mPhoneStatusBar.isInLaunchTransition();
    429     }
    430 
    431     public boolean isGoingToNotificationShade() {
    432         return mPhoneStatusBar.isGoingToNotificationShade();
    433     }
    434 
    435     public boolean isSecure(int userId) {
    436         return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
    437     }
    438 
    439     public boolean isInputRestricted() {
    440         return mViewMediatorCallback.isInputRestricted();
    441     }
    442 }
    443