Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +3 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +13 −9 Original line number Diff line number Diff line Loading @@ -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(); if (mStatusBarKeyguardViewManager.onBackPressed( isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) { if (isScrimmedBouncer || isBouncerOverDream) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } else { mNotificationPanelViewController.expandWithoutQs(); return (isScrimmedBouncer || isBouncerOverDream); } @Override public boolean onBackPressed() { if (mStatusBarKeyguardViewManager.canHandleBackPressed()) { mStatusBarKeyguardViewManager.onBackPressed(false /* unused */); return true; } if (mNotificationPanelViewController.isQsCustomizing()) { Loading @@ -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(); } Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +107 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 */); Loading Loading @@ -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()) { 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()) { 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 { mNotificationPanelViewController.expandWithoutQs(); } return true; } return false; } Loading Loading @@ -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() { Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +50 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +3 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +13 −9 Original line number Diff line number Diff line Loading @@ -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(); if (mStatusBarKeyguardViewManager.onBackPressed( isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) { if (isScrimmedBouncer || isBouncerOverDream) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } else { mNotificationPanelViewController.expandWithoutQs(); return (isScrimmedBouncer || isBouncerOverDream); } @Override public boolean onBackPressed() { if (mStatusBarKeyguardViewManager.canHandleBackPressed()) { mStatusBarKeyguardViewManager.onBackPressed(false /* unused */); return true; } if (mNotificationPanelViewController.isQsCustomizing()) { Loading @@ -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(); } Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +107 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 */); Loading Loading @@ -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()) { 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()) { 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 { mNotificationPanelViewController.expandWithoutQs(); } return true; } return false; } Loading Loading @@ -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() { Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +50 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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); Loading