summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java65
2 files changed, 120 insertions, 4 deletions
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 7da1d6c085b0..7d2928b12c72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -101,11 +101,14 @@ import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
@@ -528,10 +531,17 @@ public class CentralSurfacesImpl extends CoreStartable implements
private CentralSurfacesComponent mCentralSurfacesComponent;
// Flags for disabling the status bar
- // Two variables becaseu the first one evidently ran out of room for new flags.
+ // Two variables because the first one evidently ran out of room for new flags.
private int mDisabled1 = 0;
private int mDisabled2 = 0;
+ /**
+ * This keeps track of whether we have (or haven't) registered the predictive back callback.
+ * Since we can have visible -> visible transitions, we need to avoid
+ * double-registering (or double-unregistering) our callback.
+ */
+ private boolean mIsBackCallbackRegistered = false;
+
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -657,6 +667,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
private final InteractionJankMonitor mJankMonitor;
+ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+ if (DEBUG) {
+ Log.d(TAG, "mOnBackInvokedCallback() called");
+ }
+ onBackPressed();
+ };
/**
* Public constructor for CentralSurfaces.
@@ -2775,9 +2791,38 @@ public class CentralSurfacesImpl extends CoreStartable implements
if (visibleToUser) {
handleVisibleToUserChangedImpl(visibleToUser);
mNotificationLogger.startNotificationLogging();
+
+ if (!mIsBackCallbackRegistered) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = true;
+ if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "is now VISIBLE to user, BUT callback ALREADY unregistered");
+ }
} else {
mNotificationLogger.stopNotificationLogging();
handleVisibleToUserChangedImpl(visibleToUser);
+
+ if (mIsBackCallbackRegistered) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = false;
+ if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "is NOT VISIBLE to user, BUT NO callback (or callback ALREADY "
+ + "unregistered)");
+ }
+ }
}
}
@@ -3503,6 +3548,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
return mNotificationPanelViewController.getKeyguardBottomAreaView();
}
+ protected ViewRootImpl getViewRootImpl() {
+ NotificationShadeWindowView nswv = getNotificationShadeWindowView();
+ if (nswv != null) return nswv.getViewRootImpl();
+
+ return null;
+ }
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -3821,6 +3872,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
updateScrimController();
}
+ @VisibleForTesting
+ public void setNotificationShadeWindowViewController(
+ NotificationShadeWindowViewController nswvc) {
+ mNotificationShadeWindowViewController = nswvc;
+ }
+
/**
* Set the amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index a0f7087ddb9e..ced2dbd751c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -69,7 +70,11 @@ import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
import androidx.test.filters.SmallTest;
@@ -170,6 +175,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.MockitoAnnotations;
@@ -283,6 +289,15 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
+ /**
+ * The process of registering/unregistering a predictive back callback requires a
+ * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
+ * To prevent an NPE during test execution, we explicitly craft and provide a fake ViewRootImpl.
+ */
+ @Mock private ViewRootImpl mViewRootImpl;
+ @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
+
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -372,10 +387,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
- mShadeController = new ShadeControllerImpl(mCommandQueue,
+ mShadeController = spy(new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mCentralSurfaces), () -> mAssistManager);
+ () -> Optional.of(mCentralSurfaces), () -> mAssistManager));
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
@@ -465,7 +480,14 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mActivityLaunchAnimator,
mJankMonitor,
mDeviceStateManager,
- mWiredChargingRippleController, mDreamManager);
+ mWiredChargingRippleController, mDreamManager) {
+ @Override
+ protected ViewRootImpl getViewRootImpl() {
+ return mViewRootImpl;
+ }
+ };
+ when(mViewRootImpl.getOnBackInvokedDispatcher())
+ .thenReturn(mOnBackInvokedDispatcher);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),
@@ -743,6 +765,43 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
}
+ /**
+ * Do the following:
+ * 1. verify that a predictive back callback is registered when CSurf becomes visible
+ * 2. verify that the same callback is unregistered when CSurf becomes invisible
+ */
+ @Test
+ public void testPredictiveBackCallback_registration() {
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ mCentralSurfaces.handleVisibleToUserChanged(false);
+ verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
+ eq(mOnBackInvokedCallback.getValue()));
+ }
+
+ /**
+ * Do the following:
+ * 1. capture the predictive back callback during registration
+ * 2. call the callback directly
+ * 3. verify that the ShadeController's panel collapse animation is invoked
+ */
+ @Test
+ public void testPredictiveBackCallback_invocationCollapsesPanel() {
+ mCentralSurfaces.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
+ mOnBackInvokedCallback.getValue().onBackInvoked();
+ verify(mShadeController).animateCollapsePanels();
+ }
+
@Test
public void testPanelOpenForHeadsUp() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);