blob: 267b56378d82b0de7fc04ba99d31242ef1b9fed5 [file] [log] [blame]
/*
* 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) { }
}
}