diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 5ca7143624..5590e5c3d8 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -49,6 +49,9 @@
-->
+
+
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 3bfe047ad6..e19fd5764b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -34,6 +34,7 @@ import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_SHOW_LOCKED_TASKBAR;
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
@@ -44,12 +45,16 @@ import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Trace;
import android.provider.Settings;
import android.util.ArraySet;
@@ -116,6 +121,12 @@ public class TaskbarManager implements DisplayDecorationListener {
// TODO: b/397738606 - Remove all logs with this tag after the growth framework is integrated.
public static final String GROWTH_FRAMEWORK_TAG = "Growth Framework";
+ /**
+ * An integer extra specifying the ID of the display on which the All Apps UI should be shown
+ * or hidden.
+ */
+ public static final String EXTRA_KEY_ALL_APPS_ACTION_DISPLAY_ID =
+ "com.android.quickstep.allapps.display_id";
/**
* All the configurations which do not initiate taskbar recreation.
@@ -575,10 +586,19 @@ public class TaskbarManager implements DisplayDecorationListener {
}
/**
- * Toggles All Apps for Taskbar or Launcher depending on the current state.
+ * Shows or hides the All Apps view in the Taskbar or Launcher, based on its current
+ * visibility on the System UI tracked focused display.
*/
public void toggleAllAppsSearch() {
- TaskbarActivityContext taskbar = getTaskbarForDisplay(getFocusedDisplayId());
+ toggleAllAppsSearchForDisplay(getFocusedDisplayId());
+ }
+
+ /**
+ * Shows or hides the All Apps view in the Taskbar or Launcher, based on its current
+ * visibility on the given display, with ID {@code displayId}.
+ */
+ public void toggleAllAppsSearchForDisplay(int displayId) {
+ TaskbarActivityContext taskbar = getTaskbarForDisplay(displayId);
if (taskbar == null) {
// Home All Apps should be toggled from this class, because the controllers are not
// initialized when Taskbar is disabled (i.e. TaskbarActivityContext is null).
@@ -1713,6 +1733,28 @@ public class TaskbarManager implements DisplayDecorationListener {
debugTaskbarManager(debugReason, mPrimaryDisplayId, verbose);
}
+ /** Creates a {@link PendingIntent} for showing / hiding the all apps UI. */
+ public PendingIntent createAllAppsPendingIntent() {
+ return new PendingIntent(new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder allowlistToken, IIntentReceiver finishedReceiver,
+ String requiredPermission, Bundle options) {
+ MAIN_EXECUTOR.execute(() -> {
+ int displayId = -1;
+ if (options != null) {
+ displayId = options.getInt(EXTRA_KEY_ALL_APPS_ACTION_DISPLAY_ID, -1);
+ }
+ if (displayId == -1) {
+ toggleAllAppsSearch();
+ } else {
+ toggleAllAppsSearchForDisplay(displayId);
+ }
+ });
+ }
+ });
+ }
+
/**
* Logs verbose debug information about the TaskbarManager for a specific display.
*/
diff --git a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
index b807a4bfe3..2d179f95df 100644
--- a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
+++ b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
@@ -27,6 +27,7 @@ import android.view.accessibility.AccessibilityManager
import com.android.launcher3.R
import com.android.launcher3.util.SettingsCache
import com.android.launcher3.util.SettingsCache.OnChangeListener
+import com.android.quickstep.input.QuickstepKeyGestureEventsManager
import java.util.concurrent.Executor
private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPLETE)
@@ -41,6 +42,7 @@ private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPL
class AllAppsActionManager(
private val context: Context,
private val bgExecutor: Executor,
+ private val quickstepKeyGestureEventsManager: QuickstepKeyGestureEventsManager,
private val createAllAppsPendingIntent: () -> PendingIntent,
) {
@@ -92,17 +94,22 @@ class AllAppsActionManager(
val accessibilityManager =
context.getSystemService(AccessibilityManager::class.java) ?: return@execute
if (shouldRegisterAction) {
+ val allAppsPendingIntent = createAllAppsPendingIntent()
accessibilityManager.registerSystemAction(
RemoteAction(
Icon.createWithResource(context, R.drawable.ic_apps),
context.getString(R.string.all_apps_label),
context.getString(R.string.all_apps_label),
- createAllAppsPendingIntent(),
+ allAppsPendingIntent,
),
GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
)
+ quickstepKeyGestureEventsManager.registerAllAppsKeyGestureEvent(
+ allAppsPendingIntent
+ )
} else {
accessibilityManager.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS)
+ quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent()
}
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7bbfec9297..91587843e5 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -42,11 +42,8 @@ import static com.android.quickstep.InputConsumerUtils.newConsumer;
import static com.android.quickstep.InputConsumerUtils.tryCreateAssistantInputConsumer;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Region;
@@ -101,6 +98,7 @@ import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
+import com.android.quickstep.input.QuickstepKeyGestureEventsManager;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
@@ -639,6 +637,8 @@ public class TouchInteractionService extends Service {
private DisplayRepository mDisplayRepository;
+ private QuickstepKeyGestureEventsManager mQuickstepKeyGestureEventsHandler;
+
@Override
public void onCreate() {
super.onCreate();
@@ -653,8 +653,10 @@ public class TouchInteractionService extends Service {
mRotationTouchHelperRepository = RotationTouchHelper.REPOSITORY_INSTANCE.get(this);
mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this);
mSystemDecorationChangeObserver = SystemDecorationChangeObserver.getINSTANCE().get(this);
- mAllAppsActionManager = new AllAppsActionManager(
- this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
+ mQuickstepKeyGestureEventsHandler = new QuickstepKeyGestureEventsManager(this);
+ mAllAppsActionManager = new AllAppsActionManager(this, UI_HELPER_EXECUTOR,
+ mQuickstepKeyGestureEventsHandler,
+ () -> mTaskbarManager.createAllAppsPendingIntent());
mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
// Don't destroy and reinitialize input monitor due to trackpad
@@ -812,17 +814,6 @@ public class TouchInteractionService extends Service {
}
}
- private PendingIntent createAllAppsPendingIntent() {
- return new PendingIntent(new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllAppsSearch());
- }
- });
- }
-
@UiThread
private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags, int displayId) {
if (LockedUserState.get(this).isUserUnlocked()) {
diff --git a/quickstep/src/com/android/quickstep/input/QuickstepKeyGestureEventsManager.kt b/quickstep/src/com/android/quickstep/input/QuickstepKeyGestureEventsManager.kt
new file mode 100644
index 0000000000..d6355e9a91
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/input/QuickstepKeyGestureEventsManager.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.input
+
+import android.app.PendingIntent
+import android.content.Context
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.KeyGestureEventHandler
+import android.hardware.input.KeyGestureEvent
+import android.os.Bundle
+import android.os.IBinder
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.window.flags.Flags
+
+/**
+ * Manages subscription and unsubscription to launcher's key gesture events, e.g. all apps and
+ * recents (incl. alt + tab).
+ */
+class QuickstepKeyGestureEventsManager(context: Context) {
+ private val inputManager = requireNotNull(context.getSystemService(InputManager::class.java))
+ private var allAppsPendingIntent: PendingIntent? = null
+ @VisibleForTesting
+ val allAppsKeyGestureEventHandler =
+ object : KeyGestureEventHandler {
+ override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?) {
+ if (!Flags.enableKeyGestureHandlerForRecents()) {
+ return
+ }
+ if (event.keyGestureType != KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS) {
+ Log.e(TAG, "Ignore unsupported key gesture event type: ${event.keyGestureType}")
+ return
+ }
+
+ allAppsPendingIntent?.send(
+ Bundle().apply {
+ putInt(TaskbarManager.EXTRA_KEY_ALL_APPS_ACTION_DISPLAY_ID, event.displayId)
+ }
+ )
+ }
+ }
+
+ /** Registers the all apps key gesture events. */
+ fun registerAllAppsKeyGestureEvent(allAppsPendingIntent: PendingIntent) {
+ if (Flags.enableKeyGestureHandlerForRecents()) {
+ this.allAppsPendingIntent = allAppsPendingIntent
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS),
+ allAppsKeyGestureEventHandler,
+ )
+ }
+ }
+
+ /** Unregisters the all apps key gesture events. */
+ fun unregisterAllAppsKeyGestureEvent() {
+ if (Flags.enableKeyGestureHandlerForRecents()) {
+ inputManager.unregisterKeyGestureEventHandler(allAppsKeyGestureEventHandler)
+ }
+ }
+
+ private companion object {
+ const val TAG = "KeyGestureEventsHandler"
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index 19c88240d9..6ccc063459 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -35,6 +35,7 @@ import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.android.launcher3.util.TestUtil
import com.android.quickstep.AllAppsActionManager
import com.android.quickstep.fallback.window.RecentsDisplayModel
+import com.android.quickstep.input.QuickstepKeyGestureEventsManager
import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType
import java.util.Locale
@@ -108,7 +109,11 @@ class TaskbarUnitTestRule(
object :
TaskbarManager(
context,
- AllAppsActionManager(context, UI_HELPER_EXECUTOR) {
+ AllAppsActionManager(
+ context,
+ UI_HELPER_EXECUTOR,
+ QuickstepKeyGestureEventsManager(context),
+ ) {
PendingIntent(IIntentSender.Default())
},
object : TaskbarNavButtonCallbacks {},
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt
index a1bd107caf..a37655186e 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt
@@ -18,6 +18,7 @@ package com.android.quickstep
import android.app.PendingIntent
import android.content.IIntentSender
+import android.hardware.input.InputManager
import android.provider.Settings
import android.provider.Settings.Secure.USER_SETUP_COMPLETE
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,6 +30,7 @@ import com.android.launcher3.util.SandboxApplication
import com.android.launcher3.util.SettingsCache
import com.android.launcher3.util.SettingsCacheSandbox
import com.android.launcher3.util.TestUtil
+import com.android.quickstep.input.QuickstepKeyGestureEventsManager
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
@@ -39,6 +41,11 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.whenever
private const val TIMEOUT = 5L
private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPLETE)
@@ -49,24 +56,29 @@ class AllAppsActionManagerTest {
private val bgExecutor = UI_HELPER_EXECUTOR
@get:Rule val context = SandboxApplication()
+ private val inputManager = context.spyService(InputManager::class.java)
private val settingsCacheSandbox =
SettingsCacheSandbox().also { it[USER_SETUP_COMPLETE_URI] = 1 }
+ private val quickstepKeyGestureEventsManager = spy(QuickstepKeyGestureEventsManager(context))
private val allAppsActionManager by
lazy(LazyThreadSafetyMode.NONE) {
- AllAppsActionManager(context, bgExecutor) {
+ AllAppsActionManager(context, bgExecutor, quickstepKeyGestureEventsManager) {
callbackSemaphore.release()
PendingIntent(IIntentSender.Default())
}
}
@Before
- fun initDaggerComponent() {
+ fun setUp() {
context.initDaggerComponent(
DaggerAllAppsActionManagerTestComponent.builder()
.bindSettingsCache(settingsCacheSandbox.cache)
)
+
+ doNothing().whenever(inputManager).registerKeyGestureEventHandler(any(), any())
+ doNothing().whenever(inputManager).unregisterKeyGestureEventHandler(any())
}
@After fun destroyManager() = allAppsActionManager.onDestroy()
@@ -74,15 +86,19 @@ class AllAppsActionManagerTest {
@Test
fun taskbarPresent_actionRegistered() {
allAppsActionManager.isTaskbarPresent = true
+ TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to register.
assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ verify(quickstepKeyGestureEventsManager).registerAllAppsKeyGestureEvent(any())
}
@Test
fun homeAndOverviewSame_actionRegistered() {
allAppsActionManager.isHomeAndOverviewSame = true
+ TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to register.
assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ verify(quickstepKeyGestureEventsManager).registerAllAppsKeyGestureEvent(any())
}
@Test
@@ -93,6 +109,7 @@ class AllAppsActionManagerTest {
allAppsActionManager.isTaskbarPresent = false
TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to unregister.
assertThat(allAppsActionManager.isActionRegistered).isFalse()
+ verify(quickstepKeyGestureEventsManager).unregisterAllAppsKeyGestureEvent()
}
@Test
@@ -103,6 +120,7 @@ class AllAppsActionManagerTest {
TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to unregister.
assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
assertThat(allAppsActionManager.isActionRegistered).isFalse()
+ verify(quickstepKeyGestureEventsManager).unregisterAllAppsKeyGestureEvent()
}
@Test
@@ -136,8 +154,10 @@ class AllAppsActionManagerTest {
allAppsActionManager.isTaskbarPresent = true
settingsCacheSandbox[USER_SETUP_COMPLETE_URI] = 1
+ TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to register.
assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ verify(quickstepKeyGestureEventsManager).registerAllAppsKeyGestureEvent(any())
}
@Test
@@ -146,8 +166,10 @@ class AllAppsActionManagerTest {
allAppsActionManager.isTaskbarPresent = true
allAppsActionManager.isSetupUiVisible = false
+ TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to register.
assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ verify(quickstepKeyGestureEventsManager).registerAllAppsKeyGestureEvent(any())
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/input/QuickstepKeyGestureEventsHandlerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/input/QuickstepKeyGestureEventsHandlerTest.kt
new file mode 100644
index 0000000000..e5b517ede9
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/input/QuickstepKeyGestureEventsHandlerTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.input
+
+import android.app.PendingIntent
+import android.hardware.input.InputManager
+import android.hardware.input.KeyGestureEvent
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS
+import android.os.Bundle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.taskbar.TaskbarManager.EXTRA_KEY_ALL_APPS_ACTION_DISPLAY_ID
+import com.android.launcher3.util.SandboxApplication
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.KArgumentCaptor
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoInteractions
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QuickstepKeyGestureEventsHandlerTest {
+ @get:Rule val context = SandboxApplication()
+
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ private val inputManager = context.spyService(InputManager::class.java)
+ private val keyGestureEventsManager = QuickstepKeyGestureEventsManager(context)
+ private val allAppsPendingIntent: PendingIntent = mock()
+ private val keyGestureEventsCaptor: KArgumentCaptor> = argumentCaptor()
+ private val bundleCaptor: KArgumentCaptor = argumentCaptor()
+
+ @Before
+ fun setup() {
+ doNothing().whenever(inputManager).registerKeyGestureEventHandler(any(), any())
+ doNothing().whenever(inputManager).unregisterKeyGestureEventHandler(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ fun registerKeyGestureEventsHandler_flagEnabled_registerWithExpectedKeyGestureEvents() {
+ keyGestureEventsManager.registerAllAppsKeyGestureEvent(allAppsPendingIntent)
+
+ verify(inputManager)
+ .registerKeyGestureEventHandler(
+ keyGestureEventsCaptor.capture(),
+ eq(keyGestureEventsManager.allAppsKeyGestureEventHandler),
+ )
+ assertThat(keyGestureEventsCaptor.firstValue).containsExactly(KEY_GESTURE_TYPE_ALL_APPS)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ fun registerKeyGestureEventsHandler_flagDisabled_noRegister() {
+ keyGestureEventsManager.registerAllAppsKeyGestureEvent(allAppsPendingIntent)
+
+ verifyNoInteractions(inputManager)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ fun unregisterKeyGestureEventsHandler_flagEnabled_unregisterHandler() {
+ keyGestureEventsManager.unregisterAllAppsKeyGestureEvent()
+
+ verify(inputManager)
+ .unregisterKeyGestureEventHandler(
+ eq(keyGestureEventsManager.allAppsKeyGestureEventHandler)
+ )
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ fun unregisterKeyGestureEventsHandler_flagDisabled_noUnregister() {
+ keyGestureEventsManager.unregisterAllAppsKeyGestureEvent()
+
+ verifyNoInteractions(inputManager)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ fun handleEvent_flagEnabled_allApps_toggleAllAppsSearchWithDisplayId() {
+ keyGestureEventsManager.registerAllAppsKeyGestureEvent(allAppsPendingIntent)
+
+ keyGestureEventsManager.allAppsKeyGestureEventHandler.handleKeyGestureEvent(
+ KeyGestureEvent.Builder()
+ .setDisplayId(TEST_DISPLAY_ID)
+ .setKeyGestureType(KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ /* focusedToken= */ null,
+ )
+
+ verify(allAppsPendingIntent).send(bundleCaptor.capture())
+ assertThat(bundleCaptor.firstValue.getInt(EXTRA_KEY_ALL_APPS_ACTION_DISPLAY_ID))
+ .isEqualTo(TEST_DISPLAY_ID)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_KEY_GESTURE_HANDLER_FOR_RECENTS)
+ fun handleEvent_flagDisabled_allApps_noInteractionWithTaskbar() {
+ keyGestureEventsManager.registerAllAppsKeyGestureEvent(allAppsPendingIntent)
+
+ keyGestureEventsManager.allAppsKeyGestureEventHandler.handleKeyGestureEvent(
+ KeyGestureEvent.Builder()
+ .setDisplayId(TEST_DISPLAY_ID)
+ .setKeyGestureType(KEY_GESTURE_TYPE_ALL_APPS)
+ .build(),
+ /* focusedToken= */ null,
+ )
+
+ verifyNoInteractions(allAppsPendingIntent)
+ }
+
+ private companion object {
+ const val TEST_DISPLAY_ID = 6789
+ }
+}