summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java123
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java51
4 files changed, 173 insertions, 26 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index f37243adfa9e..fa7bfaeb6c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -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();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 0b63bbfec877..14ae008abc66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index a0578fac2033..46fc7d7c9461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -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() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index f52bfcaaaa44..fe3555fd4ec8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -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,
@@ -509,6 +527,37 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@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);
verify(mCentralSurfaces).setBouncerShowingOverDream(false);