diff options
3 files changed, 101 insertions, 1 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index ca65d12e87e6..da5819a7f3bc 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -90,6 +90,8 @@ import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.ListPopupWindow; import android.widget.TextView; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; @@ -155,6 +157,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; public static final String SYSTEM_DIALOG_REASON_DREAM = "dream"; + private static final boolean DEBUG = false; + private static final String TAG = "GlobalActionsDialogLite"; private static final String INTERACTION_JANK_TAG = "global_actions"; @@ -2177,6 +2181,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected ViewGroup mContainer; + private final OnBackInvokedCallback mOnBackInvokedCallback = () -> { + logOnBackInvocation(); + dismiss(); + }; + @VisibleForTesting protected GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { @@ -2221,6 +2230,16 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene } }; + + // this exists so that we can point it to a mock during Unit Testing + private OnBackInvokedDispatcher mOverriddenBackDispatcher; + + // the following method exists so that a Unit Test can supply a `OnBackInvokedDispatcher` + @VisibleForTesting + void setBackDispatcherOverride(OnBackInvokedDispatcher mockDispatcher) { + mOverriddenBackDispatcher = mockDispatcher; + } + ActionsDialogLite(Context context, int themeRes, MyAdapter adapter, MyOverflowAdapter overflowAdapter, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, @@ -2254,6 +2273,22 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene super.onCreate(savedInstanceState); initializeLayout(); mWindowDimAmount = getWindow().getAttributes().dimAmount; + getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback); + if (DEBUG) Log.d(TAG, "OnBackInvokedCallback handler registered"); + } + + @VisibleForTesting + @Override + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + if (mOverriddenBackDispatcher != null) return mOverriddenBackDispatcher; + else return super.getOnBackInvokedDispatcher(); + } + + @Override + public void onDetachedFromWindow() { + getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mOnBackInvokedCallback); + if (DEBUG) Log.d(TAG, "OnBackInvokedCallback handler unregistered"); } @Override @@ -2453,7 +2488,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Override public void onBackPressed() { super.onBackPressed(); + logOnBackInvocation(); + } + + private void logOnBackInvocation() { mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK); + if (DEBUG) Log.d(TAG, "onBack invoked"); } @Override diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 763a5cb810f9..ba2804572ef5 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -53,7 +53,8 @@ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" /> - <application android:debuggable="true" android:largeHeap="true"> + <application android:debuggable="true" android:largeHeap="true" + android:enableOnBackInvokedCallback="true" > <uses-library android:name="android.test.runner" /> <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver" diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 141a213a5b6a..b42b7695cedd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -42,8 +42,11 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.GestureDetector; import android.view.IWindowManager; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowManagerPolicyConstants; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import androidx.test.filters.SmallTest; @@ -73,6 +76,8 @@ import com.android.systemui.util.settings.SecureSettings; 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; @@ -117,6 +122,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private CentralSurfaces mCentralSurfaces; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private DialogLaunchAnimator mDialogLaunchAnimator; + @Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher; + @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback; private TestableLooper mTestableLooper; @@ -203,6 +210,58 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { } @Test + public void testPredictiveBackCallbackRegisteredAndUnregistered() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART, + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + + GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog(); + dialog.setBackDispatcherOverride(mOnBackInvokedDispatcher); + dialog.create(); + mTestableLooper.processAllMessages(); + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any()); + dialog.onDetachedFromWindow(); + mTestableLooper.processAllMessages(); + verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(any()); + } + + @Test + public void testPredictiveBackInvocationDismissesDialog() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART, + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + + GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog(); + dialog.create(); + dialog.show(); + mTestableLooper.processAllMessages(); + dialog.getWindow().injectInputEvent( + new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)); + dialog.getWindow().injectInputEvent( + new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)); + mTestableLooper.processAllMessages(); + verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_BACK); + assertThat(dialog.isShowing()).isFalse(); + } + + @Test public void testSingleTap_logAndDismiss() { mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); |