diff options
| author | 2022-08-23 16:02:26 -0700 | |
|---|---|---|
| committer | 2022-09-12 16:59:36 -0700 | |
| commit | a3147f13718da516659ed7e70b6c06ff1be73b4d (patch) | |
| tree | 54a413cda1331e72643132b002424ddec1bdbb48 | |
| parent | fc1f09ff31fee2f08beb9fc6298aa1d02bdb7b9f (diff) | |
Migrate CentralSurfacesImpl to Predictive Back api
This moves the CentralSurfacesImpl over to use the ahead-of-time
Predictive Back Api, which relies on callback registration and
unregistration.
Test: atest CentralSurfacesImplTest#testPredictiveBackCallback_registration and atest CentralSurfacesImplTest#testPredictiveBackCallback_invocationCollapsesPanel
Bug: 243586904
Change-Id: If7628a3375236a199025aa27cba0352ac61e747d
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);  |