Migrate StatusBarKeyguardViewManager to Predictive Back
Bug: 244635782 Test: atest StatusBarKeyguardViewManagerTest Change-Id: I94c145228e771124f05a7f565b66489d2d37f33b (cherry picked from commit 4dc8f0287691113f9b4301ef8cf226575d96e8c0)
This commit is contained in:
@@ -416,6 +416,9 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
|
||||
|
||||
void endAffordanceLaunch();
|
||||
|
||||
/** Should the keyguard be hidden immediately in response to a back press/gesture. */
|
||||
boolean shouldKeyguardHideImmediately();
|
||||
|
||||
boolean onBackPressed();
|
||||
|
||||
boolean onSpacePressed();
|
||||
|
||||
@@ -3312,19 +3312,23 @@ public class CentralSurfacesImpl extends CoreStartable implements
|
||||
mNotificationPanelViewController.onAffordanceLaunchEnded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the keyguard should hide immediately (as opposed to via an animation).
|
||||
* Non-scrimmed bouncers have a special animation tied to the notification panel expansion.
|
||||
* @return whether the keyguard should be immediately hidden.
|
||||
*/
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
public boolean shouldKeyguardHideImmediately() {
|
||||
final boolean isScrimmedBouncer =
|
||||
mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
|
||||
final boolean isBouncerOverDream = isBouncerShowingOverDream();
|
||||
return (isScrimmedBouncer || isBouncerOverDream);
|
||||
}
|
||||
|
||||
if (mStatusBarKeyguardViewManager.onBackPressed(
|
||||
isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) {
|
||||
if (isScrimmedBouncer || isBouncerOverDream) {
|
||||
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
|
||||
} else {
|
||||
mNotificationPanelViewController.expandWithoutQs();
|
||||
}
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
if (mStatusBarKeyguardViewManager.canHandleBackPressed()) {
|
||||
mStatusBarKeyguardViewManager.onBackPressed(false /* unused */);
|
||||
return true;
|
||||
}
|
||||
if (mNotificationPanelViewController.isQsCustomizing()) {
|
||||
@@ -3339,7 +3343,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
|
||||
return true;
|
||||
}
|
||||
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED
|
||||
&& !isBouncerOverDream) {
|
||||
&& !isBouncerShowingOverDream()) {
|
||||
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
|
||||
mShadeController.animateCollapsePanels();
|
||||
}
|
||||
|
||||
@@ -31,12 +31,15 @@ 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.view.WindowManagerGlobal;
|
||||
import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -119,6 +122,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
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;
|
||||
@@ -184,8 +188,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
if (mAlternateAuthInterceptor != null) {
|
||||
mAlternateAuthInterceptor.onBouncerVisibilityChanged();
|
||||
}
|
||||
|
||||
/* Register predictive back callback when keyguard becomes visible, and unregister
|
||||
when it's hidden. */
|
||||
if (isVisible) {
|
||||
registerBackCallback();
|
||||
} else {
|
||||
unregisterBackCallback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()");
|
||||
}
|
||||
onBackPressed(false /* unused */);
|
||||
};
|
||||
private boolean mIsBackCallbackRegistered = false;
|
||||
|
||||
private final DockManager.DockEventListener mDockEventListener =
|
||||
new DockManager.DockEventListener() {
|
||||
@Override
|
||||
@@ -378,6 +399,46 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
}
|
||||
|
||||
/** 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDensityOrFontScaleChanged() {
|
||||
hideBouncer(true /* destroyView */);
|
||||
@@ -1015,25 +1076,47 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this manager that the back button has been pressed.
|
||||
* Returns whether a back invocation can be handled, which depends on whether the keyguard
|
||||
* is currently showing (which itself is derived from multiple states).
|
||||
*
|
||||
* @param hideImmediately Hide bouncer when {@code true}, keep it around otherwise.
|
||||
* Non-scrimmed bouncers have a special animation tied to the expansion
|
||||
* of the notification panel.
|
||||
* @return whether the back press has been handled
|
||||
* @return whether a back press can be handled right now.
|
||||
*/
|
||||
public boolean onBackPressed(boolean hideImmediately) {
|
||||
if (bouncerIsShowing()) {
|
||||
mCentralSurfaces.endAffordanceLaunch();
|
||||
// The second condition is for SIM card locked bouncer
|
||||
if (bouncerIsScrimmed()
|
||||
&& !needsFullscreenBouncer()) {
|
||||
hideBouncer(false);
|
||||
updateStates();
|
||||
public boolean canHandleBackPressed() {
|
||||
return mBouncer.isShowing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this manager that the back button has been pressed.
|
||||
*/
|
||||
// TODO(b/244635782): This "accept boolean and ignore it, and always return false" was done
|
||||
// to make it possible to check this in *and* allow merging to master,
|
||||
// where ArcStatusBarKeyguardViewManager inherits this class, and its
|
||||
// build will break if we change this interface.
|
||||
// So, overall, while this function refactors the behavior of onBackPressed,
|
||||
// (it now handles the back press, and no longer returns *whether* it did so)
|
||||
// its interface is not changing right now (but will, in a follow-up CL).
|
||||
public boolean onBackPressed(boolean ignored) {
|
||||
if (!canHandleBackPressed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mCentralSurfaces.endAffordanceLaunch();
|
||||
// The second condition is for SIM card locked bouncer
|
||||
if (bouncerIsScrimmed() && 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 {
|
||||
reset(hideImmediately);
|
||||
mNotificationPanelViewController.expandWithoutQs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1304,7 +1387,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
@Override
|
||||
public ViewRootImpl getViewRootImpl() {
|
||||
return mNotificationShadeWindowController.getNotificationShadeView().getViewRootImpl();
|
||||
ViewGroup viewGroup = mNotificationShadeWindowController.getNotificationShadeView();
|
||||
if (viewGroup != null) {
|
||||
return viewGroup.getViewRootImpl();
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "ViewGroup was null, cannot get ViewRootImpl");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void launchPendingWakeupAction() {
|
||||
|
||||
@@ -35,6 +35,10 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
import android.window.WindowOnBackInvokedDispatcher;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -73,6 +77,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
@@ -119,6 +124,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
|
||||
|
||||
@Mock private ViewRootImpl mViewRootImpl;
|
||||
@Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
|
||||
@Captor
|
||||
private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -154,7 +165,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
mFeatureFlags,
|
||||
mBouncerCallbackInteractor,
|
||||
mBouncerInteractor,
|
||||
mBouncerView);
|
||||
mBouncerView) {
|
||||
@Override
|
||||
public ViewRootImpl getViewRootImpl() {
|
||||
return mViewRootImpl;
|
||||
}
|
||||
};
|
||||
when(mViewRootImpl.getOnBackInvokedDispatcher())
|
||||
.thenReturn(mOnBackInvokedDispatcher);
|
||||
mStatusBarKeyguardViewManager.registerCentralSurfaces(
|
||||
mCentralSurfaces,
|
||||
mNotificationPanelView,
|
||||
@@ -508,6 +526,37 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
fraction, expanded, tracking, /* dragDownPxAmount= */ 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPredictiveBackCallback_registration() {
|
||||
/* verify that a predictive back callback is registered when the bouncer becomes visible */
|
||||
mBouncerExpansionCallback.onVisibilityChanged(true);
|
||||
verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
|
||||
eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
|
||||
mOnBackInvokedCallback.capture());
|
||||
|
||||
/* verify that the same callback is unregistered when the bouncer becomes invisible */
|
||||
mBouncerExpansionCallback.onVisibilityChanged(false);
|
||||
verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
|
||||
eq(mOnBackInvokedCallback.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPredictiveBackCallback_invocationHidesBouncer() {
|
||||
mBouncerExpansionCallback.onVisibilityChanged(true);
|
||||
/* capture the predictive back callback during registration */
|
||||
verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
|
||||
eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
|
||||
mOnBackInvokedCallback.capture());
|
||||
|
||||
when(mBouncer.isShowing()).thenReturn(true);
|
||||
when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
|
||||
/* invoke the back callback directly */
|
||||
mOnBackInvokedCallback.getValue().onBackInvoked();
|
||||
|
||||
/* verify that the bouncer will be hidden as a result of the invocation */
|
||||
verify(mCentralSurfaces).setBouncerShowing(eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReportBouncerOnDreamWhenVisible() {
|
||||
mBouncerExpansionCallback.onVisibilityChanged(true);
|
||||
|
||||
Reference in New Issue
Block a user