| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.systemui.statusbar.phone; |
| |
| import static android.view.WindowInsets.Type.navigationBars; |
| |
| import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; |
| import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; |
| import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; |
| import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; |
| import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; |
| |
| import android.content.Context; |
| import android.content.res.ColorStateList; |
| import android.hardware.biometrics.BiometricSourceType; |
| import android.os.Bundle; |
| import android.os.SystemClock; |
| import android.os.Trace; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewRootImpl; |
| import android.window.BackEvent; |
| import android.window.OnBackAnimationCallback; |
| import android.window.OnBackInvokedDispatcher; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.internal.util.LatencyTracker; |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.keyguard.AuthKeyguardMessageArea; |
| import com.android.keyguard.KeyguardMessageAreaController; |
| import com.android.keyguard.KeyguardSecurityModel; |
| import com.android.keyguard.KeyguardUpdateMonitor; |
| import com.android.keyguard.KeyguardUpdateMonitorCallback; |
| import com.android.keyguard.KeyguardViewController; |
| import com.android.keyguard.TrustGrantFlags; |
| import com.android.keyguard.ViewMediatorCallback; |
| import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; |
| import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; |
| import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; |
| import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; |
| import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; |
| import com.android.systemui.bouncer.ui.BouncerView; |
| import com.android.systemui.dagger.SysUISingleton; |
| import com.android.systemui.dagger.qualifiers.Main; |
| import com.android.systemui.dock.DockManager; |
| import com.android.systemui.dreams.DreamOverlayStateController; |
| import com.android.systemui.flags.FeatureFlags; |
| import com.android.systemui.flags.Flags; |
| import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor; |
| import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; |
| import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor; |
| import com.android.systemui.keyguard.shared.model.DismissAction; |
| import com.android.systemui.keyguard.shared.model.KeyguardDone; |
| import com.android.systemui.navigationbar.NavigationBarView; |
| import com.android.systemui.navigationbar.NavigationModeController; |
| import com.android.systemui.navigationbar.TaskbarDelegate; |
| import com.android.systemui.plugins.ActivityStarter; |
| import com.android.systemui.plugins.statusbar.StatusBarStateController; |
| import com.android.systemui.shade.ShadeController; |
| import com.android.systemui.shade.ShadeExpansionChangeEvent; |
| import com.android.systemui.shade.ShadeExpansionListener; |
| import com.android.systemui.shade.ShadeExpansionStateManager; |
| import com.android.systemui.shade.ShadeViewController; |
| import com.android.systemui.shared.system.QuickStepContract; |
| import com.android.systemui.shared.system.SysUiStatsLog; |
| import com.android.systemui.statusbar.NotificationMediaManager; |
| import com.android.systemui.statusbar.NotificationShadeWindowController; |
| import com.android.systemui.statusbar.RemoteInputController; |
| import com.android.systemui.statusbar.StatusBarState; |
| import com.android.systemui.statusbar.SysuiStatusBarStateController; |
| import com.android.systemui.statusbar.policy.ConfigurationController; |
| import com.android.systemui.statusbar.policy.KeyguardStateController; |
| import com.android.systemui.unfold.FoldAodAnimationController; |
| import com.android.systemui.unfold.SysUIUnfoldComponent; |
| import com.android.systemui.user.domain.interactor.SelectedUserInteractor; |
| |
| import dagger.Lazy; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.Set; |
| |
| import javax.inject.Inject; |
| |
| import kotlinx.coroutines.CoroutineDispatcher; |
| import kotlinx.coroutines.ExperimentalCoroutinesApi; |
| |
| /** |
| * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back |
| * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, |
| * which is in turn, reported to this class by the current |
| * {@link com.android.keyguard.KeyguardViewController}. |
| */ |
| @ExperimentalCoroutinesApi @SysUISingleton |
| public class StatusBarKeyguardViewManager implements RemoteInputController.Callback, |
| StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener, |
| ShadeExpansionListener, NavigationModeController.ModeChangedListener, |
| KeyguardViewController, FoldAodAnimationController.FoldAodAnimationStatus { |
| |
| // When hiding the Keyguard with timing supplied from WindowManager, better be early than late. |
| private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3; |
| |
| // Delay for showing the navigation bar when the bouncer appears. This should be kept in sync |
| // with the appear animations of the PIN/pattern/password views. |
| private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320; |
| |
| // The duration to fade the nav bar content in/out when the device starts to sleep |
| private static final long NAV_BAR_CONTENT_FADE_DURATION = 125; |
| |
| // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to |
| // make everything a bit slower to bridge a gap until the user is unlocked and home screen has |
| // dranw its first frame. |
| private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000; |
| |
| private static final String TAG = "StatusBarKeyguardViewManager"; |
| private static final boolean DEBUG = false; |
| |
| protected final Context mContext; |
| private final ConfigurationController mConfigurationController; |
| private final NavigationModeController mNavigationModeController; |
| private final NotificationShadeWindowController mNotificationShadeWindowController; |
| private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory; |
| private final DreamOverlayStateController mDreamOverlayStateController; |
| @Nullable |
| private final FoldAodAnimationController mFoldAodAnimationController; |
| KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController; |
| private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; |
| private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; |
| private final AlternateBouncerInteractor mAlternateBouncerInteractor; |
| private final BouncerView mPrimaryBouncerView; |
| private final Lazy<ShadeController> mShadeController; |
| |
| // Local cache of expansion events, to avoid duplicates |
| private float mFraction = -1f; |
| private boolean mTracking = false; |
| private boolean mBouncerShowingOverDream; |
| |
| private final PrimaryBouncerExpansionCallback mExpansionCallback = |
| new PrimaryBouncerExpansionCallback() { |
| private boolean mPrimaryBouncerAnimating; |
| |
| @Override |
| public void onFullyShown() { |
| mPrimaryBouncerAnimating = false; |
| updateStates(); |
| } |
| |
| @Override |
| public void onStartingToHide() { |
| mPrimaryBouncerAnimating = true; |
| updateStates(); |
| } |
| |
| @Override |
| public void onStartingToShow() { |
| mPrimaryBouncerAnimating = true; |
| updateStates(); |
| } |
| |
| @Override |
| public void onFullyHidden() { |
| mPrimaryBouncerAnimating = false; |
| updateStates(); |
| } |
| |
| @Override |
| public void onExpansionChanged(float expansion) { |
| if (mPrimaryBouncerAnimating) { |
| mCentralSurfaces.setPrimaryBouncerHiddenFraction(expansion); |
| } |
| } |
| |
| @Override |
| public void onVisibilityChanged(boolean isVisible) { |
| mBouncerShowingOverDream = |
| isVisible && mDreamOverlayStateController.isOverlayActive(); |
| |
| if (!isVisible) { |
| mCentralSurfaces.setPrimaryBouncerHiddenFraction(EXPANSION_HIDDEN); |
| } |
| |
| /* Register predictive back callback when keyguard becomes visible, and unregister |
| when it's hidden. */ |
| if (isVisible) { |
| registerBackCallback(); |
| } else { |
| unregisterBackCallback(); |
| } |
| } |
| }; |
| |
| private final OnBackAnimationCallback mOnBackInvokedCallback = new OnBackAnimationCallback() { |
| @Override |
| public void onBackInvoked() { |
| if (DEBUG) { |
| Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()"); |
| } |
| onBackPressed(); |
| if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { |
| mPrimaryBouncerView.getDelegate().getBackCallback().onBackInvoked(); |
| } |
| } |
| |
| @Override |
| public void onBackProgressed(BackEvent event) { |
| if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { |
| mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event); |
| } |
| } |
| |
| @Override |
| public void onBackCancelled() { |
| if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { |
| mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled(); |
| } |
| } |
| |
| @Override |
| public void onBackStarted(BackEvent event) { |
| if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { |
| mPrimaryBouncerView.getDelegate().getBackCallback().onBackStarted(event); |
| } |
| } |
| }; |
| private boolean mIsBackCallbackRegistered = false; |
| |
| private final DockManager.DockEventListener mDockEventListener = |
| new DockManager.DockEventListener() { |
| @Override |
| public void onEvent(int event) { |
| boolean isDocked = mDockManager.isDocked(); |
| if (isDocked == mIsDocked) { |
| return; |
| } |
| mIsDocked = isDocked; |
| updateStates(); |
| } |
| }; |
| |
| protected LockPatternUtils mLockPatternUtils; |
| protected ViewMediatorCallback mViewMediatorCallback; |
| @Nullable protected CentralSurfaces mCentralSurfaces; |
| private ShadeViewController mShadeViewController; |
| private BiometricUnlockController mBiometricUnlockController; |
| private boolean mCentralSurfacesRegistered; |
| |
| private View mNotificationContainer; |
| |
| protected boolean mRemoteInputActive; |
| private boolean mGlobalActionsVisible = false; |
| private boolean mLastGlobalActionsVisible = false; |
| private boolean mDozing; |
| private boolean mPulsing; |
| private boolean mGesturalNav; |
| private boolean mIsDocked; |
| private boolean mScreenOffAnimationPlaying; |
| |
| protected boolean mFirstUpdate = true; |
| protected boolean mLastShowing; |
| protected boolean mLastOccluded; |
| private boolean mLastPrimaryBouncerShowing; |
| private boolean mLastPrimaryBouncerIsOrWillBeShowing; |
| private boolean mLastBouncerDismissible; |
| protected boolean mLastRemoteInputActive; |
| private boolean mLastDozing; |
| private boolean mLastGesturalNav; |
| private boolean mLastIsDocked; |
| private boolean mLastPulsing; |
| private int mLastBiometricMode; |
| private boolean mLastScreenOffAnimationPlaying; |
| private float mQsExpansion; |
| |
| private FeatureFlags mFlags; |
| |
| final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>(); |
| private boolean mIsBackAnimationEnabled; |
| private final UdfpsOverlayInteractor mUdfpsOverlayInteractor; |
| private final ActivityStarter mActivityStarter; |
| |
| private OnDismissAction mAfterKeyguardGoneAction; |
| private Runnable mKeyguardGoneCancelAction; |
| private boolean mDismissActionWillAnimateOnKeyguard; |
| private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>(); |
| |
| // Dismiss action to be launched when we stop dozing or the keyguard is gone. |
| private DismissWithActionRequest mPendingWakeupAction; |
| private final KeyguardStateController mKeyguardStateController; |
| private final NotificationMediaManager mMediaManager; |
| private final SysuiStatusBarStateController mStatusBarStateController; |
| private final DockManager mDockManager; |
| private final KeyguardUpdateMonitor mKeyguardUpdateManager; |
| private final LatencyTracker mLatencyTracker; |
| private final KeyguardSecurityModel mKeyguardSecurityModel; |
| private final SelectedUserInteractor mSelectedUserInteractor; |
| @Nullable private KeyguardBypassController mBypassController; |
| @Nullable private OccludingAppBiometricUI mOccludingAppBiometricUI; |
| |
| @Nullable private TaskbarDelegate mTaskbarDelegate; |
| private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = |
| new KeyguardUpdateMonitorCallback() { |
| @Override |
| public void onTrustGrantedForCurrentUser( |
| boolean dismissKeyguard, |
| boolean newlyUnlocked, |
| @NonNull TrustGrantFlags flags, |
| @Nullable String message |
| ) { |
| updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); |
| } |
| |
| @Override |
| public void onEmergencyCallAction() { |
| // Since we won't get a setOccluded call we have to reset the view manually such that |
| // the bouncer goes away. |
| if (mKeyguardStateController.isOccluded()) { |
| reset(true /* hideBouncerWhenShowing */); |
| } |
| } |
| }; |
| private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor; |
| private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor; |
| |
| @Inject |
| public StatusBarKeyguardViewManager( |
| Context context, |
| ViewMediatorCallback callback, |
| LockPatternUtils lockPatternUtils, |
| SysuiStatusBarStateController sysuiStatusBarStateController, |
| ConfigurationController configurationController, |
| KeyguardUpdateMonitor keyguardUpdateMonitor, |
| DreamOverlayStateController dreamOverlayStateController, |
| NavigationModeController navigationModeController, |
| DockManager dockManager, |
| NotificationShadeWindowController notificationShadeWindowController, |
| KeyguardStateController keyguardStateController, |
| NotificationMediaManager notificationMediaManager, |
| KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, |
| Optional<SysUIUnfoldComponent> sysUIUnfoldComponent, |
| Lazy<ShadeController> shadeController, |
| LatencyTracker latencyTracker, |
| KeyguardSecurityModel keyguardSecurityModel, |
| FeatureFlags featureFlags, |
| PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor, |
| PrimaryBouncerInteractor primaryBouncerInteractor, |
| BouncerView primaryBouncerView, |
| AlternateBouncerInteractor alternateBouncerInteractor, |
| UdfpsOverlayInteractor udfpsOverlayInteractor, |
| ActivityStarter activityStarter, |
| KeyguardTransitionInteractor keyguardTransitionInteractor, |
| @Main CoroutineDispatcher mainDispatcher, |
| Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor, |
| Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy, |
| SelectedUserInteractor selectedUserInteractor |
| ) { |
| mContext = context; |
| mViewMediatorCallback = callback; |
| mLockPatternUtils = lockPatternUtils; |
| mConfigurationController = configurationController; |
| mNavigationModeController = navigationModeController; |
| mNotificationShadeWindowController = notificationShadeWindowController; |
| mDreamOverlayStateController = dreamOverlayStateController; |
| mKeyguardStateController = keyguardStateController; |
| mMediaManager = notificationMediaManager; |
| mKeyguardUpdateManager = keyguardUpdateMonitor; |
| mStatusBarStateController = sysuiStatusBarStateController; |
| mDockManager = dockManager; |
| mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; |
| mShadeController = shadeController; |
| mLatencyTracker = latencyTracker; |
| mKeyguardSecurityModel = keyguardSecurityModel; |
| mFlags = featureFlags; |
| mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor; |
| mPrimaryBouncerInteractor = primaryBouncerInteractor; |
| mPrimaryBouncerView = primaryBouncerView; |
| mFoldAodAnimationController = sysUIUnfoldComponent |
| .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); |
| mAlternateBouncerInteractor = alternateBouncerInteractor; |
| mIsBackAnimationEnabled = |
| featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM); |
| mUdfpsOverlayInteractor = udfpsOverlayInteractor; |
| mActivityStarter = activityStarter; |
| mKeyguardTransitionInteractor = keyguardTransitionInteractor; |
| mMainDispatcher = mainDispatcher; |
| mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor; |
| mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy; |
| mSelectedUserInteractor = selectedUserInteractor; |
| } |
| |
| KeyguardTransitionInteractor mKeyguardTransitionInteractor; |
| CoroutineDispatcher mMainDispatcher; |
| |
| @Override |
| public void registerCentralSurfaces(CentralSurfaces centralSurfaces, |
| ShadeViewController shadeViewController, |
| ShadeExpansionStateManager shadeExpansionStateManager, |
| BiometricUnlockController biometricUnlockController, |
| View notificationContainer, |
| KeyguardBypassController bypassController) { |
| mCentralSurfaces = centralSurfaces; |
| mBiometricUnlockController = biometricUnlockController; |
| |
| mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback); |
| mShadeViewController = shadeViewController; |
| if (shadeExpansionStateManager != null) { |
| ShadeExpansionChangeEvent currentState = |
| shadeExpansionStateManager.addExpansionListener(this); |
| onPanelExpansionChanged(currentState); |
| } |
| mBypassController = bypassController; |
| mNotificationContainer = notificationContainer; |
| mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create( |
| centralSurfaces.getKeyguardMessageArea()); |
| mCentralSurfacesRegistered = true; |
| |
| registerListeners(); |
| } |
| |
| |
| /** |
| * Sets the given OccludingAppBiometricUI to null if it's the current auth interceptor. Else, |
| * does nothing. |
| */ |
| public void removeOccludingAppBiometricUI(@NonNull OccludingAppBiometricUI biometricUI) { |
| if (Objects.equals(mOccludingAppBiometricUI, biometricUI)) { |
| mOccludingAppBiometricUI = null; |
| } |
| } |
| |
| /** |
| * Sets a new OccludingAppBiometricUI. |
| */ |
| public void setOccludingAppBiometricUI(@NonNull OccludingAppBiometricUI biometricUI) { |
| if (!Objects.equals(mOccludingAppBiometricUI, biometricUI)) { |
| mOccludingAppBiometricUI = biometricUI; |
| } |
| } |
| |
| private void registerListeners() { |
| mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback); |
| mStatusBarStateController.addCallback(this); |
| mConfigurationController.addCallback(this); |
| mGesturalNav = QuickStepContract.isGesturalMode( |
| mNavigationModeController.addListener(this)); |
| if (mFoldAodAnimationController != null) { |
| mFoldAodAnimationController.addCallback(this); |
| } |
| if (mDockManager != null) { |
| mDockManager.addListener(mDockEventListener); |
| mIsDocked = mDockManager.isDocked(); |
| } |
| |
| if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { |
| // Show the keyguard views whenever we've told WM that the lockscreen is visible. |
| mShadeViewController.postToView(() -> |
| collectFlow( |
| getViewRootImpl().getView(), |
| mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(), |
| this::consumeShowStatusBarKeyguardView)); |
| } |
| } |
| |
| private void consumeShowStatusBarKeyguardView(boolean show) { |
| if (show != mLastShowing) { |
| if (show) { |
| show(null); |
| } else { |
| hide(0, 0); |
| } |
| } |
| } |
| |
| /** Register a callback, to be invoked by the Predictive Back system. */ |
| private void registerBackCallback() { |
| if (!mIsBackCallbackRegistered) { |
| ViewRootImpl viewRoot = getViewRootImpl(); |
| if (viewRoot != null) { |
| viewRoot.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( |
| OnBackInvokedDispatcher.PRIORITY_OVERLAY, mOnBackInvokedCallback); |
| mIsBackCallbackRegistered = true; |
| } else { |
| if (DEBUG) { |
| Log.d(TAG, "view root was null, could not register back callback"); |
| } |
| } |
| } else { |
| if (DEBUG) { |
| Log.d(TAG, "prevented registering back callback twice"); |
| } |
| } |
| } |
| |
| /** Unregister the callback formerly registered with the Predictive Back system. */ |
| private void unregisterBackCallback() { |
| if (mIsBackCallbackRegistered) { |
| ViewRootImpl viewRoot = getViewRootImpl(); |
| if (viewRoot != null) { |
| viewRoot.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback( |
| mOnBackInvokedCallback); |
| mIsBackCallbackRegistered = false; |
| } else { |
| if (DEBUG) { |
| Log.d(TAG, "view root was null, could not unregister back callback"); |
| } |
| } |
| } else { |
| if (DEBUG) { |
| Log.d(TAG, "prevented unregistering back callback twice"); |
| } |
| } |
| } |
| |
| private boolean shouldPlayBackAnimation() { |
| // Suppress back animation when bouncer shouldn't be dismissed on back invocation. |
| return !needsFullscreenBouncer() && mIsBackAnimationEnabled; |
| } |
| |
| @Override |
| public void onDensityOrFontScaleChanged() { |
| hideBouncer(true /* destroyView */); |
| } |
| |
| private boolean beginShowingBouncer(ShadeExpansionChangeEvent event) { |
| // Avoid having the shade and the bouncer open at the same time over a dream. |
| final boolean hideBouncerOverDream = |
| mDreamOverlayStateController.isOverlayActive() |
| && (mShadeViewController.isExpanded() |
| || mShadeViewController.isExpandingOrCollapsing()); |
| |
| final boolean isUserTrackingStarted = |
| event.getFraction() != EXPANSION_HIDDEN && event.getTracking(); |
| |
| return mKeyguardStateController.isShowing() |
| && !primaryBouncerIsOrWillBeShowing() |
| && !mKeyguardStateController.isKeyguardGoingAway() |
| && isUserTrackingStarted |
| && !hideBouncerOverDream |
| && !mKeyguardStateController.isOccluded() |
| && !mKeyguardStateController.canDismissLockScreen() |
| && !bouncerIsAnimatingAway() |
| && !(mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED); |
| } |
| |
| @Override |
| public void onPanelExpansionChanged(ShadeExpansionChangeEvent event) { |
| float fraction = event.getFraction(); |
| boolean tracking = event.getTracking(); |
| |
| if (mFraction == fraction && mTracking == tracking) { |
| // Ignore duplicate events, as they will cause confusion with bouncer expansion |
| return; |
| } |
| mFraction = fraction; |
| mTracking = tracking; |
| |
| /* |
| * The bouncer may have received a call to show(), or the following will infer it from |
| * device state and touch handling. The bouncer MUST have been notified that it is about to |
| * show if any subsequent events are to be handled. |
| */ |
| if (beginShowingBouncer(event)) { |
| mPrimaryBouncerInteractor.show(/* isScrimmed= */false); |
| } |
| |
| if (!primaryBouncerIsOrWillBeShowing()) { |
| return; |
| } |
| |
| if (mKeyguardStateController.isShowing()) { |
| mPrimaryBouncerInteractor.setPanelExpansion(fraction); |
| } else { |
| mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN); |
| } |
| } |
| |
| /** |
| * Update the global actions visibility state in order to show the navBar when active. |
| */ |
| public void setGlobalActionsVisible(boolean isVisible) { |
| mGlobalActionsVisible = isVisible; |
| updateStates(); |
| } |
| |
| public void setTaskbarDelegate(TaskbarDelegate taskbarDelegate) { |
| mTaskbarDelegate = taskbarDelegate; |
| } |
| |
| /** |
| * Show the keyguard. Will handle creating and attaching to the view manager |
| * lazily. |
| */ |
| @Override |
| public void show(Bundle options) { |
| Trace.beginSection("StatusBarKeyguardViewManager#show"); |
| mNotificationShadeWindowController.setKeyguardShowing(true); |
| mKeyguardStateController.notifyKeyguardState(true, |
| mKeyguardStateController.isOccluded()); |
| reset(true /* hideBouncerWhenShowing */); |
| SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, |
| SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); |
| Trace.endSection(); |
| } |
| |
| /** |
| * Shows the notification keyguard or the bouncer depending on |
| * {@link #needsFullscreenBouncer()}. |
| */ |
| protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) { |
| if (needsFullscreenBouncer() && !mDozing) { |
| // The keyguard might be showing (already). So we need to hide it. |
| if (!primaryBouncerIsShowing()) { |
| mCentralSurfaces.hideKeyguard(); |
| mPrimaryBouncerInteractor.show(true); |
| } else { |
| Log.e(TAG, "Attempted to show the sim bouncer when it is already showing."); |
| } |
| } else { |
| mCentralSurfaces.showKeyguard(); |
| if (hideBouncerWhenShowing) { |
| hideBouncer(false /* destroyView */); |
| } |
| } |
| updateStates(); |
| } |
| |
| /** |
| * |
| * If possible, shows the alternate bouncer. Else, shows the primary (pin/pattern/password) |
| * bouncer. |
| * @param scrimmed true when the primary bouncer should show scrimmed, |
| * false when the user will be dragging it and translation should be deferred |
| * {@see KeyguardBouncer#show(boolean, boolean)} |
| */ |
| public void showBouncer(boolean scrimmed) { |
| if (!mAlternateBouncerInteractor.show()) { |
| showPrimaryBouncer(scrimmed); |
| } else { |
| updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); |
| } |
| } |
| |
| /** |
| * Hides the input bouncer (pin/password/pattern). |
| */ |
| @VisibleForTesting |
| void hideBouncer(boolean destroyView) { |
| mPrimaryBouncerInteractor.hide(); |
| if (mKeyguardStateController.isShowing()) { |
| // If we were showing the bouncer and then aborting, we need to also clear out any |
| // potential actions unless we actually unlocked. |
| cancelPostAuthActions(); |
| } |
| cancelPendingWakeupAction(); |
| } |
| |
| /** |
| * Shows the primary bouncer - the pin/pattern/password challenge on the lock screen. |
| * |
| * @param scrimmed true when the bouncer should show scrimmed, false when the user will be |
| * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} |
| */ |
| public void showPrimaryBouncer(boolean scrimmed) { |
| hideAlternateBouncer(false); |
| if (mKeyguardStateController.isShowing() && !isBouncerShowing()) { |
| mPrimaryBouncerInteractor.show(scrimmed); |
| } |
| updateStates(); |
| } |
| |
| public void dismissWithAction(OnDismissAction r, Runnable cancelAction, |
| boolean afterKeyguardGone) { |
| dismissWithAction(r, cancelAction, afterKeyguardGone, null /* message */); |
| } |
| |
| public void dismissWithAction(OnDismissAction r, Runnable cancelAction, |
| boolean afterKeyguardGone, String message) { |
| if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) { |
| if (r == null) { |
| return; |
| } |
| Trace.beginSection("StatusBarKeyguardViewManager#interactorDismissWithAction"); |
| if (afterKeyguardGone) { |
| mKeyguardDismissActionInteractor.get().setDismissAction( |
| new DismissAction.RunAfterKeyguardGone( |
| () -> { |
| r.onDismiss(); |
| return null; |
| }, |
| (cancelAction != null) ? cancelAction : () -> {}, |
| message == null ? "" : message, |
| r.willRunAnimationOnKeyguard() |
| ) |
| ); |
| } else { |
| mKeyguardDismissActionInteractor.get().setDismissAction( |
| new DismissAction.RunImmediately( |
| () -> { |
| if (r.onDismiss()) { |
| return KeyguardDone.LATER; |
| } else { |
| return KeyguardDone.IMMEDIATE; |
| } |
| }, |
| (cancelAction != null) ? cancelAction : () -> {}, |
| message == null ? "" : message, |
| r.willRunAnimationOnKeyguard() |
| ) |
| ); |
| } |
| |
| showBouncer(true); |
| Trace.endSection(); |
| return; |
| } |
| |
| if (mKeyguardStateController.isShowing()) { |
| try { |
| Trace.beginSection("StatusBarKeyguardViewManager#dismissWithAction"); |
| cancelPendingWakeupAction(); |
| // If we're dozing, this needs to be delayed until after we wake up - unless we're |
| // wake-and-unlocking, because there dozing will last until the end of the |
| // transition. |
| if (mDozing && !isWakeAndUnlocking()) { |
| mPendingWakeupAction = new DismissWithActionRequest( |
| r, cancelAction, afterKeyguardGone, message); |
| return; |
| } |
| |
| if (!mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) { |
| mAfterKeyguardGoneAction = r; |
| mKeyguardGoneCancelAction = cancelAction; |
| mDismissActionWillAnimateOnKeyguard = r != null |
| && r.willRunAnimationOnKeyguard(); |
| } |
| |
| // If there is an alternate auth interceptor (like the UDFPS), show that one |
| // instead of the bouncer. |
| if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { |
| if (!afterKeyguardGone) { |
| mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction, |
| mKeyguardGoneCancelAction); |
| mAfterKeyguardGoneAction = null; |
| mKeyguardGoneCancelAction = null; |
| } |
| |
| updateAlternateBouncerShowing(mAlternateBouncerInteractor.show()); |
| setKeyguardMessage(message, null); |
| return; |
| } |
| |
| mViewMediatorCallback.setCustomMessage(message); |
| if (afterKeyguardGone) { |
| // we'll handle the dismiss action after keyguard is gone, so just show the |
| // bouncer |
| mPrimaryBouncerInteractor.show(/* isScrimmed= */true); |
| } else { |
| // after authentication success, run dismiss action with the option to defer |
| // hiding the keyguard based on the return value of the OnDismissAction |
| mPrimaryBouncerInteractor.setDismissAction( |
| mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); |
| mPrimaryBouncerInteractor.show(/* isScrimmed= */true); |
| // bouncer will handle the dismiss action, so we no longer need to track it here |
| mAfterKeyguardGoneAction = null; |
| mKeyguardGoneCancelAction = null; |
| } |
| } finally { |
| Trace.endSection(); |
| } |
| } |
| updateStates(); |
| } |
| |
| private boolean isWakeAndUnlocking() { |
| int mode = mBiometricUnlockController.getMode(); |
| return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING; |
| } |
| |
| /** |
| * Adds a {@param runnable} to be executed after Keyguard is gone. |
| */ |
| public void addAfterKeyguardGoneRunnable(Runnable runnable) { |
| if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) { |
| if (runnable != null) { |
| mKeyguardDismissActionInteractor.get().runAfterKeyguardGone(runnable); |
| } |
| return; |
| } |
| mAfterKeyguardGoneRunnables.add(runnable); |
| } |
| |
| @Override |
| public void reset(boolean hideBouncerWhenShowing) { |
| if (mKeyguardStateController.isShowing() && !bouncerIsAnimatingAway()) { |
| final boolean isOccluded = mKeyguardStateController.isOccluded(); |
| // Hide quick settings. |
| mShadeViewController.resetViews(/* animate= */ !isOccluded); |
| // Hide bouncer and quick-quick settings. |
| if (isOccluded && !mDozing) { |
| mCentralSurfaces.hideKeyguard(); |
| if (hideBouncerWhenShowing || needsFullscreenBouncer()) { |
| hideBouncer(false /* destroyView */); |
| } |
| } else { |
| showBouncerOrKeyguard(hideBouncerWhenShowing); |
| } |
| if (hideBouncerWhenShowing) { |
| hideAlternateBouncer(true); |
| } |
| mKeyguardUpdateManager.sendKeyguardReset(); |
| updateStates(); |
| } |
| } |
| |
| @Override |
| public void hideAlternateBouncer(boolean updateScrim) { |
| updateAlternateBouncerShowing(mAlternateBouncerInteractor.hide() && updateScrim); |
| } |
| |
| private void updateAlternateBouncerShowing(boolean updateScrim) { |
| if (!mCentralSurfacesRegistered) { |
| // if CentralSurfaces hasn't been registered yet, then the controllers below haven't |
| // been initialized yet so there's no need to attempt to forward them events. |
| return; |
| } |
| |
| final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState(); |
| if (mKeyguardMessageAreaController != null) { |
| mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer); |
| mKeyguardMessageAreaController.setMessage(""); |
| } |
| mBypassController.setAltBouncerShowing(isShowingAlternateBouncer); |
| mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer); |
| |
| if (updateScrim) { |
| mCentralSurfaces.updateScrimController(); |
| } |
| } |
| |
| @Override |
| public void onStartedWakingUp() { |
| mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() |
| .setAnimationsDisabled(false); |
| NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView(); |
| if (navBarView != null) { |
| navBarView.forEachView(view -> |
| view.animate() |
| .alpha(1f) |
| .setDuration(NAV_BAR_CONTENT_FADE_DURATION) |
| .start()); |
| } |
| } |
| |
| @Override |
| public void onStartedGoingToSleep() { |
| mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() |
| .setAnimationsDisabled(true); |
| NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView(); |
| if (navBarView != null) { |
| navBarView.forEachView(view -> |
| view.animate() |
| .alpha(0f) |
| .setDuration(NAV_BAR_CONTENT_FADE_DURATION) |
| .start()); |
| } |
| } |
| |
| @Override |
| public void onFinishedGoingToSleep() { |
| mPrimaryBouncerInteractor.hide(); |
| } |
| |
| @Override |
| public void onRemoteInputActive(boolean active) { |
| mRemoteInputActive = active; |
| updateStates(); |
| } |
| |
| private void setDozing(boolean dozing) { |
| if (mDozing != dozing) { |
| mDozing = dozing; |
| if (dozing || needsFullscreenBouncer() |
| || mKeyguardStateController.isOccluded()) { |
| reset(dozing /* hideBouncerWhenShowing */); |
| } |
| updateStates(); |
| |
| if (!dozing) { |
| launchPendingWakeupAction(); |
| } |
| } |
| } |
| |
| /** |
| * If {@link CentralSurfaces} is pulsing. |
| */ |
| public void setPulsing(boolean pulsing) { |
| if (mPulsing != pulsing) { |
| mPulsing = pulsing; |
| updateStates(); |
| } |
| } |
| |
| @Override |
| public void setNeedsInput(boolean needsInput) { |
| mNotificationShadeWindowController.setKeyguardNeedsInput(needsInput); |
| } |
| |
| @Override |
| public boolean isUnlockWithWallpaper() { |
| return mNotificationShadeWindowController.isShowingWallpaper(); |
| } |
| |
| @Override |
| public boolean isBouncerShowingOverDream() { |
| return mBouncerShowingOverDream; |
| } |
| |
| @Override |
| public void setOccluded(boolean occluded, boolean animate) { |
| final boolean wasOccluded = mKeyguardStateController.isOccluded(); |
| final boolean isOccluding = !wasOccluded && occluded; |
| final boolean isUnOccluding = wasOccluded && !occluded; |
| mKeyguardStateController.notifyKeyguardState( |
| mKeyguardStateController.isShowing(), occluded); |
| updateStates(); |
| final boolean isShowing = mKeyguardStateController.isShowing(); |
| final boolean isOccluded = mKeyguardStateController.isOccluded(); |
| |
| if (isShowing && isOccluding) { |
| SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, |
| SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED); |
| if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) { |
| // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post |
| // collapse runnables will be run. |
| mShadeController.get().addPostCollapseAction(() -> { |
| mNotificationShadeWindowController.setKeyguardOccluded(isOccluded); |
| reset(true /* hideBouncerWhenShowing */); |
| }); |
| return; |
| } |
| } else if (isShowing && isUnOccluding) { |
| SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, |
| SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); |
| } |
| mNotificationShadeWindowController.setKeyguardOccluded(isOccluded); |
| |
| // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's |
| // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause |
| // unexpected visible behavior if the keyguard is still visible as we're animating unlocked. |
| if (!mDozing && !mKeyguardStateController.isKeyguardGoingAway()) { |
| // If Keyguard is reshown, don't hide the bouncer as it might just have been requested |
| // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. |
| reset(isOccluding /* hideBouncerWhenShowing*/); |
| } |
| } |
| |
| @Override |
| public void startPreHideAnimation(Runnable finishRunnable) { |
| if (primaryBouncerIsShowing()) { |
| mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable); |
| mShadeViewController.startBouncerPreHideAnimation(); |
| |
| // We update the state (which will show the keyguard) only if an animation will run on |
| // the keyguard. If there is no animation, we wait before updating the state so that we |
| // go directly from bouncer to launcher/app. |
| if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) { |
| if (mKeyguardDismissActionInteractor.get().runDismissAnimationOnKeyguard()) { |
| updateStates(); |
| } |
| } else if (mDismissActionWillAnimateOnKeyguard) { |
| updateStates(); |
| } |
| } else if (finishRunnable != null) { |
| finishRunnable.run(); |
| } |
| mShadeViewController.blockExpansionForCurrentTouch(); |
| } |
| |
| @Override |
| public void blockPanelExpansionFromCurrentTouch() { |
| mShadeViewController.blockExpansionForCurrentTouch(); |
| } |
| |
| @Override |
| public void hide(long startTime, long fadeoutDuration) { |
| Trace.beginSection("StatusBarKeyguardViewManager#hide"); |
| mKeyguardStateController.notifyKeyguardState(false, |
| mKeyguardStateController.isOccluded()); |
| launchPendingWakeupAction(); |
| |
| if (mKeyguardUpdateManager.needsSlowUnlockTransition()) { |
| fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED; |
| } |
| long uptimeMillis = SystemClock.uptimeMillis(); |
| long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis); |
| |
| if (mKeyguardStateController.isFlingingToDismissKeyguard()) { |
| final boolean wasFlingingToDismissKeyguard = |
| mKeyguardStateController.isFlingingToDismissKeyguard(); |
| mCentralSurfaces.fadeKeyguardAfterLaunchTransition(new Runnable() { |
| @Override |
| public void run() { |
| mNotificationShadeWindowController.setKeyguardShowing(false); |
| mNotificationShadeWindowController.setKeyguardFadingAway(true); |
| hideBouncer(true /* destroyView */); |
| updateStates(); |
| } |
| }, /* endRunnable */ new Runnable() { |
| @Override |
| public void run() { |
| mCentralSurfaces.hideKeyguard(); |
| mNotificationShadeWindowController.setKeyguardFadingAway(false); |
| |
| if (wasFlingingToDismissKeyguard) { |
| mCentralSurfaces.finishKeyguardFadingAway(); |
| } |
| |
| mViewMediatorCallback.keyguardGone(); |
| executeAfterKeyguardGoneAction(); |
| } |
| }, /* cancelRunnable */ new Runnable() { |
| @Override |
| public void run() { |
| mNotificationShadeWindowController.setKeyguardFadingAway(false); |
| if (wasFlingingToDismissKeyguard) { |
| mCentralSurfaces.finishKeyguardFadingAway(); |
| } |
| cancelPostAuthActions(); |
| } |
| }); |
| } else { |
| executeAfterKeyguardGoneAction(); |
| mCentralSurfaces.setKeyguardFadingAway(startTime, delay, fadeoutDuration); |
| mBiometricUnlockController.startKeyguardFadingAway(); |
| hideBouncer(true /* destroyView */); |
| |
| boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide(); |
| if (!staying) { |
| mNotificationShadeWindowController.setKeyguardFadingAway(true); |
| wakeAndUnlockDejank(); |
| mCentralSurfaces.hideKeyguard(); |
| // hide() will happen asynchronously and might arrive after the scrims |
| // were already hidden, this means that the transition callback won't |
| // be triggered anymore and StatusBarWindowController will be forever in |
| // the fadingAway state. |
| mCentralSurfaces.updateScrimController(); |
| } else { |
| mCentralSurfaces.hideKeyguard(); |
| mCentralSurfaces.finishKeyguardFadingAway(); |
| mBiometricUnlockController.finishKeyguardFadingAway(); |
| } |
| |
| updateStates(); |
| mNotificationShadeWindowController.setKeyguardShowing(false); |
| mViewMediatorCallback.keyguardGone(); |
| } |
| SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, |
| SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN); |
| Trace.endSection(); |
| } |
| |
| @Override |
| public void onNavigationModeChanged(int mode) { |
| boolean gesturalNav = QuickStepContract.isGesturalMode(mode); |
| if (gesturalNav != mGesturalNav) { |
| mGesturalNav = gesturalNav; |
| updateStates(); |
| } |
| } |
| |
| public void onThemeChanged() { |
| updateResources(); |
| } |
| |
| public void onKeyguardFadedAway() { |
| mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController |
| .setKeyguardFadingAway(false), 100); |
| mShadeViewController.resetViewGroupFade(); |
| mCentralSurfaces.finishKeyguardFadingAway(); |
| mBiometricUnlockController.finishKeyguardFadingAway(); |
| } |
| |
| private void wakeAndUnlockDejank() { |
| if (mBiometricUnlockController.isWakeAndUnlock() && mLatencyTracker.isEnabled()) { |
| BiometricSourceType type = mBiometricUnlockController.getBiometricType(); |
| mLatencyTracker.onActionEnd(type == BiometricSourceType.FACE |
| ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK |
| : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK); |
| } |
| } |
| |
| private void executeAfterKeyguardGoneAction() { |
| if (mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)) { |
| return; |
| } |
| if (mAfterKeyguardGoneAction != null) { |
| mAfterKeyguardGoneAction.onDismiss(); |
| mAfterKeyguardGoneAction = null; |
| } |
| mKeyguardGoneCancelAction = null; |
| mDismissActionWillAnimateOnKeyguard = false; |
| for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) { |
| mAfterKeyguardGoneRunnables.get(i).run(); |
| } |
| mAfterKeyguardGoneRunnables.clear(); |
| } |
| |
| @Override |
| public void dismissAndCollapse() { |
| mActivityStarter.executeRunnableDismissingKeyguard( |
| /* runnable= */ null, |
| /* cancelAction= */ null, |
| /* dismissShade= */ true, |
| /* afterKeyguardGone= */ false, |
| /* deferred= */ true |
| ); |
| } |
| |
| /** |
| * WARNING: This method might cause Binder calls. |
| */ |
| public boolean isSecure() { |
| return mKeyguardSecurityModel.getSecurityMode( |
| mSelectedUserInteractor.getSelectedUserId()) |
| != KeyguardSecurityModel.SecurityMode.None; |
| } |
| |
| /** |
| * Returns whether a back invocation can be handled, which depends on whether the keyguard |
| * is currently showing (which itself is derived from multiple states). |
| * |
| * @return whether a back press can be handled right now. |
| */ |
| public boolean canHandleBackPressed() { |
| return primaryBouncerIsShowing(); |
| } |
| |
| /** |
| * Notifies this manager that the back button has been pressed. |
| */ |
| public void onBackPressed() { |
| if (!canHandleBackPressed()) { |
| return; |
| } |
| |
| mCentralSurfaces.endAffordanceLaunch(); |
| // The second condition is for SIM card locked bouncer |
| if (primaryBouncerIsScrimmed() && !needsFullscreenBouncer()) { |
| hideBouncer(false); |
| updateStates(); |
| } else { |
| /* Non-scrimmed bouncers have a special animation tied to the expansion |
| * of the notification panel. We decide whether to kick this animation off |
| * by computing the hideImmediately boolean. |
| */ |
| boolean hideImmediately = mCentralSurfaces.shouldKeyguardHideImmediately(); |
| reset(hideImmediately); |
| if (hideImmediately) { |
| mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); |
| } else { |
| mShadeViewController.expandToNotifications(); |
| } |
| } |
| return; |
| } |
| |
| @Override |
| public boolean isBouncerShowing() { |
| return primaryBouncerIsShowing() || mAlternateBouncerInteractor.isVisibleState(); |
| } |
| |
| @Override |
| public boolean primaryBouncerIsOrWillBeShowing() { |
| return isBouncerShowing() || isPrimaryBouncerInTransit(); |
| } |
| |
| public boolean isFullscreenBouncer() { |
| return mPrimaryBouncerView.getDelegate() != null |
| && mPrimaryBouncerView.getDelegate().isFullScreenBouncer(); |
| } |
| |
| /** |
| * Clear out any potential actions that were saved to run when the device is unlocked |
| */ |
| public void cancelPostAuthActions() { |
| if (primaryBouncerIsOrWillBeShowing()) { |
| return; // allow the primary bouncer to trigger saved actions |
| } |
| mAfterKeyguardGoneAction = null; |
| mDismissActionWillAnimateOnKeyguard = false; |
| if (mKeyguardGoneCancelAction != null) { |
| mKeyguardGoneCancelAction.run(); |
| mKeyguardGoneCancelAction = null; |
| } |
| } |
| |
| private long getNavBarShowDelay() { |
| if (mKeyguardStateController.isKeyguardFadingAway()) { |
| return mKeyguardStateController.getKeyguardFadingAwayDelay(); |
| } else if (isBouncerShowing()) { |
| return NAV_BAR_SHOW_DELAY_BOUNCER; |
| } else { |
| // No longer dozing, or remote input is active. No delay. |
| return 0; |
| } |
| } |
| |
| private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() { |
| @Override |
| public void run() { |
| NavigationBarView view = mCentralSurfaces.getNavigationBarView(); |
| if (view != null) { |
| view.setVisibility(View.VISIBLE); |
| } |
| mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() |
| .show(navigationBars()); |
| } |
| }; |
| |
| protected void updateStates() { |
| if (!mCentralSurfacesRegistered) { |
| return; |
| } |
| boolean showing = mKeyguardStateController.isShowing(); |
| boolean occluded = mKeyguardStateController.isOccluded(); |
| boolean primaryBouncerShowing = primaryBouncerIsShowing(); |
| boolean primaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing(); |
| boolean primaryBouncerDismissible = !isFullscreenBouncer(); |
| boolean remoteInputActive = mRemoteInputActive; |
| |
| if ((primaryBouncerDismissible || !showing || remoteInputActive) |
| != (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive) |
| || mFirstUpdate) { |
| if (primaryBouncerDismissible || !showing || remoteInputActive) { |
| mPrimaryBouncerInteractor.setBackButtonEnabled(true); |
| } else { |
| mPrimaryBouncerInteractor.setBackButtonEnabled(false); |
| } |
| } |
| |
| boolean navBarVisible = isNavBarVisible(); |
| boolean lastNavBarVisible = getLastNavBarVisible(); |
| if (navBarVisible != lastNavBarVisible || mFirstUpdate) { |
| updateNavigationBarVisibility(navBarVisible); |
| } |
| |
| boolean isPrimaryBouncerShowingChanged = |
| primaryBouncerShowing != mLastPrimaryBouncerShowing; |
| mLastPrimaryBouncerShowing = primaryBouncerShowing; |
| |
| if (isPrimaryBouncerShowingChanged || mFirstUpdate) { |
| mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing); |
| mCentralSurfaces.setBouncerShowing(primaryBouncerShowing); |
| } |
| if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate |
| || isPrimaryBouncerShowingChanged) { |
| mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing, |
| primaryBouncerShowing); |
| } |
| |
| mFirstUpdate = false; |
| mLastShowing = showing; |
| mLastGlobalActionsVisible = mGlobalActionsVisible; |
| mLastOccluded = occluded; |
| mLastPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing; |
| mLastBouncerDismissible = primaryBouncerDismissible; |
| mLastRemoteInputActive = remoteInputActive; |
| mLastDozing = mDozing; |
| mLastPulsing = mPulsing; |
| mLastScreenOffAnimationPlaying = mScreenOffAnimationPlaying; |
| mLastBiometricMode = mBiometricUnlockController.getMode(); |
| mLastGesturalNav = mGesturalNav; |
| mLastIsDocked = mIsDocked; |
| mCentralSurfaces.onKeyguardViewManagerStatesUpdated(); |
| } |
| |
| /** |
| * Updates the visibility of the nav bar window (which will cause insets changes). |
| */ |
| protected void updateNavigationBarVisibility(boolean navBarVisible) { |
| if (mCentralSurfaces.getNavigationBarView() != null |
| || (mTaskbarDelegate != null && mTaskbarDelegate.isInitialized())) { |
| if (navBarVisible) { |
| long delay = getNavBarShowDelay(); |
| if (delay == 0) { |
| mMakeNavigationBarVisibleRunnable.run(); |
| } else { |
| mNotificationContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable, |
| delay); |
| } |
| } else { |
| mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable); |
| mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() |
| .hide(navigationBars()); |
| } |
| } |
| } |
| |
| /** |
| * @return Whether the navigation bar should be made visible based on the current state. |
| */ |
| public boolean isNavBarVisible() { |
| boolean isWakeAndUnlockPulsing = mBiometricUnlockController != null |
| && mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING; |
| boolean keyguardVisible = mKeyguardStateController.isVisible(); |
| boolean hideWhileDozing = mDozing && !isWakeAndUnlockPulsing; |
| boolean keyguardWithGestureNav = (keyguardVisible && !mDozing && !mScreenOffAnimationPlaying |
| || mPulsing && !mIsDocked) |
| && mGesturalNav; |
| return (!keyguardVisible && !hideWhileDozing && !mScreenOffAnimationPlaying |
| || primaryBouncerIsShowing() |
| || mRemoteInputActive |
| || keyguardWithGestureNav |
| || mGlobalActionsVisible); |
| } |
| |
| /** |
| * @return Whether the navigation bar was made visible based on the last known state. |
| */ |
| protected boolean getLastNavBarVisible() { |
| boolean keyguardShowing = mLastShowing && !mLastOccluded; |
| boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING; |
| boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing |
| && !mLastScreenOffAnimationPlaying || mLastPulsing && !mLastIsDocked) |
| && mLastGesturalNav; |
| return (!keyguardShowing && !hideWhileDozing && !mLastScreenOffAnimationPlaying |
| || mLastPrimaryBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav |
| || mLastGlobalActionsVisible); |
| } |
| |
| public boolean shouldDismissOnMenuPressed() { |
| return mPrimaryBouncerView.getDelegate() != null |
| && mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed(); |
| } |
| |
| public boolean interceptMediaKey(KeyEvent event) { |
| return mPrimaryBouncerView.getDelegate() != null |
| && mPrimaryBouncerView.getDelegate().interceptMediaKey(event); |
| } |
| |
| /** |
| * @return true if the pre IME back event should be handled |
| */ |
| public boolean dispatchBackKeyEventPreIme() { |
| return mPrimaryBouncerView.getDelegate() != null |
| && mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme(); |
| } |
| |
| public void readyForKeyguardDone() { |
| mViewMediatorCallback.readyForKeyguardDone(); |
| } |
| |
| @Override |
| public boolean shouldDisableWindowAnimationsForUnlock() { |
| return false; |
| } |
| |
| @Override |
| public boolean shouldSubtleWindowAnimationsForUnlock() { |
| return false; |
| } |
| |
| @Override |
| public boolean isGoingToNotificationShade() { |
| return mStatusBarStateController.leaveOpenOnKeyguardHide(); |
| } |
| |
| public boolean isSecure(int userId) { |
| return isSecure() || mLockPatternUtils.isSecure(userId); |
| } |
| |
| @Override |
| public void keyguardGoingAway() { |
| mCentralSurfaces.keyguardGoingAway(); |
| } |
| |
| @Override |
| public void setKeyguardGoingAwayState(boolean isKeyguardGoingAway) { |
| mNotificationShadeWindowController.setKeyguardGoingAway(isKeyguardGoingAway); |
| } |
| |
| @Override |
| public void onCancelClicked() { |
| // No-op |
| } |
| |
| /** |
| * Notifies that the user has authenticated by other means than using the bouncer, for example, |
| * fingerprint and the keyguard should immediately dismiss. |
| */ |
| public void notifyKeyguardAuthenticated(boolean strongAuth) { |
| mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedBiometrics(strongAuth); |
| |
| if (mAlternateBouncerInteractor.isVisibleState()) { |
| hideAlternateBouncer(false); |
| executeAfterKeyguardGoneAction(); |
| } |
| |
| if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { |
| mKeyguardTransitionInteractor.startDismissKeyguardTransition(); |
| } |
| } |
| |
| /** Display security message to relevant KeyguardMessageArea. */ |
| public void setKeyguardMessage(String message, ColorStateList colorState) { |
| if (mAlternateBouncerInteractor.isVisibleState()) { |
| if (mKeyguardMessageAreaController != null) { |
| mKeyguardMessageAreaController.setMessage(message); |
| } |
| } else { |
| mPrimaryBouncerInteractor.showMessage(message, colorState); |
| } |
| } |
| |
| @Override |
| public ViewRootImpl getViewRootImpl() { |
| ViewGroup viewGroup = mNotificationShadeWindowController.getWindowRootView(); |
| if (viewGroup != null) { |
| return viewGroup.getViewRootImpl(); |
| } else { |
| if (DEBUG) { |
| Log.d(TAG, "ViewGroup was null, cannot get ViewRootImpl"); |
| } |
| return null; |
| } |
| } |
| |
| public void launchPendingWakeupAction() { |
| DismissWithActionRequest request = mPendingWakeupAction; |
| mPendingWakeupAction = null; |
| if (request != null) { |
| if (mKeyguardStateController.isShowing()) { |
| dismissWithAction(request.dismissAction, request.cancelAction, |
| request.afterKeyguardGone, request.message); |
| } else if (request.dismissAction != null) { |
| request.dismissAction.onDismiss(); |
| } |
| } |
| } |
| |
| public void cancelPendingWakeupAction() { |
| DismissWithActionRequest request = mPendingWakeupAction; |
| mPendingWakeupAction = null; |
| if (request != null && request.cancelAction != null) { |
| request.cancelAction.run(); |
| } |
| } |
| |
| /** |
| * Whether the primary bouncer requires scrimming. |
| */ |
| public boolean primaryBouncerNeedsScrimming() { |
| // When a dream overlay is active, scrimming will cause any expansion to immediately expand. |
| return (mKeyguardStateController.isOccluded() |
| && !mDreamOverlayStateController.isOverlayActive()) |
| || primaryBouncerWillDismissWithAction() |
| || (primaryBouncerIsShowing() && primaryBouncerIsScrimmed()) |
| || isFullscreenBouncer(); |
| } |
| |
| /** |
| * Apply keyguard configuration from the currently active resources. This can be called when the |
| * device configuration changes, to re-apply some resources that are qualified on the device |
| * configuration. |
| */ |
| public void updateResources() { |
| mPrimaryBouncerInteractor.updateResources(); |
| } |
| |
| public void dump(PrintWriter pw) { |
| pw.println("StatusBarKeyguardViewManager:"); |
| pw.println(" mRemoteInputActive: " + mRemoteInputActive); |
| pw.println(" mDozing: " + mDozing); |
| pw.println(" mAfterKeyguardGoneAction: " + mAfterKeyguardGoneAction); |
| pw.println(" mAfterKeyguardGoneRunnables: " + mAfterKeyguardGoneRunnables); |
| pw.println(" mPendingWakeupAction: " + mPendingWakeupAction); |
| pw.println(" isBouncerShowing(): " + isBouncerShowing()); |
| pw.println(" bouncerIsOrWillBeShowing(): " + primaryBouncerIsOrWillBeShowing()); |
| pw.println(" Registered KeyguardViewManagerCallbacks:"); |
| pw.println(" refactorKeyguardDismissIntent enabled:" |
| + mFlags.isEnabled(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT)); |
| for (KeyguardViewManagerCallback callback : mCallbacks) { |
| pw.println(" " + callback); |
| } |
| |
| if (mOccludingAppBiometricUI != null) { |
| pw.println("mOccludingAppBiometricUI:"); |
| mOccludingAppBiometricUI.dump(pw); |
| } |
| } |
| |
| @Override |
| public void onDozingChanged(boolean isDozing) { |
| setDozing(isDozing); |
| } |
| |
| @Override |
| public void onFoldToAodAnimationChanged() { |
| if (mFoldAodAnimationController != null) { |
| mScreenOffAnimationPlaying = mFoldAodAnimationController.shouldPlayAnimation(); |
| } |
| } |
| |
| /** |
| * Add a callback to listen for changes |
| */ |
| public void addCallback(KeyguardViewManagerCallback callback) { |
| mCallbacks.add(callback); |
| } |
| |
| /** |
| * Removes callback to stop receiving updates |
| */ |
| public void removeCallback(KeyguardViewManagerCallback callback) { |
| mCallbacks.remove(callback); |
| } |
| |
| /** |
| * Whether qs is currently expanded. |
| */ |
| public float getQsExpansion() { |
| return mQsExpansion; |
| } |
| |
| /** |
| * Update qs expansion. |
| */ |
| public void setQsExpansion(float qsExpansion) { |
| mQsExpansion = qsExpansion; |
| for (KeyguardViewManagerCallback callback : mCallbacks) { |
| callback.onQSExpansionChanged(mQsExpansion); |
| } |
| } |
| |
| /** |
| * An opportunity for the AlternateBouncer to handle the touch instead of sending |
| * the touch to NPVC child views. |
| * @return true if the alternate bouncer should consime the touch and prevent it from |
| * going to its child views |
| */ |
| public boolean dispatchTouchEvent(MotionEvent event) { |
| if (shouldInterceptTouchEvent(event) |
| && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) { |
| onTouch(event); |
| } |
| return shouldInterceptTouchEvent(event); |
| } |
| |
| /** |
| * Whether the touch should be intercepted by the AlternateBouncer before going to the |
| * notification shade's child views. |
| */ |
| public boolean shouldInterceptTouchEvent(MotionEvent event) { |
| if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { |
| return false; |
| } |
| return mAlternateBouncerInteractor.isVisibleState(); |
| } |
| |
| /** |
| * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently |
| * showing. |
| */ |
| public boolean onTouch(MotionEvent event) { |
| if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) { |
| return false; |
| } |
| |
| boolean handleTouch = shouldInterceptTouchEvent(event); |
| if (handleTouch) { |
| final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN; |
| final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch() |
| && event.getActionMasked() == MotionEvent.ACTION_UP; |
| final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade = |
| mKeyguardUpdateManager.isUdfpsEnrolled(); |
| final boolean actionOutsideShouldDismissAlternateBouncer = |
| event.getActionMasked() == MotionEvent.ACTION_OUTSIDE |
| && !udfpsOverlayWillForwardEventsOutsideNotificationShade; |
| if (actionDown) { |
| mAlternateBouncerInteractor.setReceivedDownTouch(true); |
| } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer) |
| && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) { |
| showPrimaryBouncer(true); |
| } |
| } |
| |
| // Forward NPVC touches to callbacks in case they want to respond to touches |
| for (KeyguardViewManagerCallback callback: mCallbacks) { |
| callback.onTouch(event); |
| } |
| |
| return handleTouch; |
| } |
| |
| /** Update keyguard position based on a tapped X coordinate. */ |
| public void updateKeyguardPosition(float x) { |
| mPrimaryBouncerInteractor.setKeyguardPosition(x); |
| } |
| |
| private static class DismissWithActionRequest { |
| final OnDismissAction dismissAction; |
| final Runnable cancelAction; |
| final boolean afterKeyguardGone; |
| final String message; |
| |
| DismissWithActionRequest(OnDismissAction dismissAction, Runnable cancelAction, |
| boolean afterKeyguardGone, String message) { |
| this.dismissAction = dismissAction; |
| this.cancelAction = cancelAction; |
| this.afterKeyguardGone = afterKeyguardGone; |
| this.message = message; |
| } |
| } |
| |
| /** |
| * Request to authenticate using face. |
| */ |
| public void requestFace(boolean request) { |
| mKeyguardUpdateManager.requestFaceAuthOnOccludingApp(request); |
| } |
| |
| /** |
| * Request to authenticate using the fingerprint sensor. If the fingerprint sensor is udfps, |
| * uses the color provided by udfpsColor for the fingerprint icon. |
| */ |
| public void requestFp(boolean request, int udfpsColor) { |
| mKeyguardUpdateManager.requestFingerprintAuthOnOccludingApp(request); |
| if (mOccludingAppBiometricUI != null) { |
| mOccludingAppBiometricUI.requestUdfps(request, udfpsColor); |
| } |
| } |
| |
| /** |
| * Returns if bouncer expansion is between 0 and 1 non-inclusive. |
| */ |
| public boolean isPrimaryBouncerInTransit() { |
| return mPrimaryBouncerInteractor.isInTransit(); |
| } |
| |
| /** |
| * Returns if bouncer is showing |
| */ |
| public boolean primaryBouncerIsShowing() { |
| return mPrimaryBouncerInteractor.isFullyShowing(); |
| } |
| |
| /** |
| * Returns if bouncer is scrimmed |
| */ |
| public boolean primaryBouncerIsScrimmed() { |
| return mPrimaryBouncerInteractor.isScrimmed(); |
| } |
| |
| /** |
| * Returns if bouncer is animating away |
| */ |
| public boolean bouncerIsAnimatingAway() { |
| return mPrimaryBouncerInteractor.isAnimatingAway(); |
| } |
| |
| /** |
| * Returns if bouncer will dismiss with action |
| */ |
| public boolean primaryBouncerWillDismissWithAction() { |
| return mPrimaryBouncerInteractor.willDismissWithAction(); |
| } |
| |
| /** |
| * Returns if bouncer needs fullscreen bouncer. i.e. sim pin security method |
| */ |
| public boolean needsFullscreenBouncer() { |
| KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode( |
| mSelectedUserInteractor.getSelectedUserId()); |
| return mode == KeyguardSecurityModel.SecurityMode.SimPin |
| || mode == KeyguardSecurityModel.SecurityMode.SimPuk; |
| } |
| |
| /** |
| * Delegate used to send show and hide events to an alternate authentication method instead of |
| * the regular pin/pattern/password bouncer. |
| */ |
| public interface OccludingAppBiometricUI { |
| /** |
| * Use when an app occluding the keyguard would like to give the user ability to |
| * unlock the device using udfps. |
| * |
| * @param color of the udfps icon. should have proper contrast with its background. only |
| * used if requestUdfps = true |
| */ |
| void requestUdfps(boolean requestUdfps, int color); |
| |
| /** |
| * print information for the alternate bouncer registered |
| */ |
| void dump(PrintWriter pw); |
| } |
| |
| /** |
| * Callback for KeyguardViewManager state changes. |
| */ |
| public interface KeyguardViewManagerCallback { |
| /** |
| * Set the amount qs is expanded. For example, swipe down from the top of the |
| * lock screen to start the full QS expansion. |
| */ |
| default void onQSExpansionChanged(float qsExpansion) { } |
| |
| /** |
| * Forward touch events to callbacks |
| */ |
| default void onTouch(MotionEvent event) { } |
| } |
| } |