/* * 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 static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows; 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.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor; 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 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; /** * 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}. */ @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 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 mKeyguardMessageAreaController; private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final BouncerView mPrimaryBouncerView; private final Lazy 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 mCallbacks = new HashSet<>(); private boolean mIsBackAnimationEnabled; private final boolean mUdfpsNewTouchDetectionEnabled; private final UdfpsOverlayInteractor mUdfpsOverlayInteractor; private final ActivityStarter mActivityStarter; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; private boolean mDismissActionWillAnimateOnKeyguard; private final ArrayList 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; @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 mWmLockscreenVisibilityInteractor; @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, Lazy 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 wmLockscreenVisibilityInteractor ) { 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); mUdfpsNewTouchDetectionEnabled = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION); mUdfpsOverlayInteractor = udfpsOverlayInteractor; mActivityStarter = activityStarter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mMainDispatcher = mainDispatcher; mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor; } 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)) { mShadeViewController.postToView(() -> collectFlow( getViewRootImpl().getView(), combineFlows( mKeyguardTransitionInteractor.getFinishedKeyguardState(), mWmLockscreenVisibilityInteractor.get() .getUsingKeyguardGoingAwayAnimation(), (finishedState, animating) -> KeyguardInteractor.Companion.isKeyguardVisibleInState( finishedState) || animating), 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() && !mShadeViewController.isUnlockHintRunning() && !(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. mCentralSurfaces.hideKeyguard(); mPrimaryBouncerInteractor.show(true); } 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 (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; } 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) { 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); } if (isShowing) { mMediaManager.updateMediaMetaData(false, animate && !isOccluded); } 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 (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 (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( KeyguardUpdateMonitor.getCurrentUser()) != 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. */ public void notifyKeyguardAuthenticated(boolean strongAuth) { mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(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:"); 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) { 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) { 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 = mUdfpsNewTouchDetectionEnabled && 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( KeyguardUpdateMonitor.getCurrentUser()); 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) { } } }