Merge "Migrate StatusBarKeyguardViewManager to Predictive Back" into tm-qpr-dev am: fd0c9bfa3b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20093581

Change-Id: I62ed10f89e15870a33f0faacda4ce9ac8432d63b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Rahul Banerjee
2022-10-10 17:27:34 +00:00
committed by Automerger Merge Worker
4 changed files with 173 additions and 26 deletions

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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 */);
@@ -1025,25 +1086,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;
}
@@ -1318,7 +1401,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() {

View File

@@ -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);