More test for taskbar overflow
Adds tests to TaskbarOverflowTests to verify that:
* tapping overflow view toggles keyboard quick switch view
* tasks associated with a hotseat item don't get added to
recents taskbar section / overflow view
* keyboard quick switch view shown from taskbar contains
running tasks, excluding tasks associated with a hotseat item
Bug: 379774843
Flag: com.android.launcher3.taskbar_overflow
Test: atest TaskbarOverflowTest
Change-Id: Iee6316c33cef6322d567e853f9fa358b7af9e172
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 5afc5ed..8555376 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.view.MotionEvent;
@@ -354,6 +355,27 @@
}
}
+ @VisibleForTesting
+ boolean isShownFromTaskbar() {
+ return isShown() && mQuickSwitchViewController.wasOpenedFromTaskbar();
+ }
+
+ @VisibleForTesting
+ boolean isShown() {
+ return mQuickSwitchViewController != null
+ && !mQuickSwitchViewController.isCloseAnimationRunning();
+ }
+
+ @VisibleForTesting
+ List<Integer> shownTaskIds() {
+ if (!isShown()) {
+ return Collections.emptyList();
+ }
+
+ return mTasks.stream().flatMap(
+ groupTask -> groupTask.getTasks().stream().map(task -> task.key.id)).toList();
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "KeyboardQuickSwitchController:");
@@ -423,7 +445,13 @@
if (task == null) {
return false;
}
- int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId;
+ ActivityManager.RunningTaskInfo runningTaskInfo =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ if (runningTaskInfo == null) {
+ return false;
+ }
+
+ int runningTaskId = runningTaskInfo.taskId;
return task.containsTask(runningTaskId);
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index 3a27bb1..3761044 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -19,6 +19,7 @@
import android.animation.AnimatorTestRule
import android.content.ComponentName
import android.content.Intent
+import android.os.Process
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -27,6 +28,7 @@
import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
import com.android.launcher3.R
import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatItems
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
@@ -111,6 +113,7 @@
@InjectController lateinit var recentAppsController: TaskbarRecentAppsController
@InjectController lateinit var bubbleBarViewController: BubbleBarViewController
@InjectController lateinit var bubbleStashController: BubbleStashController
+ @InjectController lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
private var desktopTaskListener: IDesktopTaskListener? = null
@@ -209,8 +212,10 @@
runOnMainSync {
val taskbarView: TaskbarView =
taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
+ val hotseatItems = createHotseatItems(maxNumberOfTaskbarIcons - initialIconCount)
+
taskbarView.updateItems(
- createHotseatItems(maxNumberOfTaskbarIcons - initialIconCount),
+ recentAppsController.updateHotseatItemInfos(hotseatItems as Array<ItemInfo?>),
recentAppsController.shownTasks,
)
}
@@ -327,13 +332,122 @@
assertThat(taskbarIconsCentered).isTrue()
}
+ @Test
+ @TaskbarMode(PINNED)
+ fun testPressingOverflowButtonOpensKeyboardQuickSwitch() {
+ val maxNumIconViews = maxNumberOfTaskbarIcons
+ // Assume there are at least all apps and divider icon, as they would appear once running
+ // apps are added, even if not present initially.
+ val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
+
+ val targetOverflowSize = 5
+ val createdTasks = maxNumIconViews - initialIconCount + targetOverflowSize
+ createDesktopTask(createdTasks)
+
+ assertThat(taskbarOverflowIconIndex).isEqualTo(initialIconCount)
+ tapOverflowIcon()
+ // Keyboard quick switch view is shown only after list of recent task is asynchronously
+ // retrieved from the recents model.
+ runOnMainSync { recentsModel.resolvePendingTaskRequests() }
+
+ assertThat(getOnUiThread { keyboardQuickSwitchController.isShownFromTaskbar }).isTrue()
+ assertThat(getOnUiThread { keyboardQuickSwitchController.shownTaskIds() })
+ .containsExactlyElementsIn(0..<createdTasks)
+
+ tapOverflowIcon()
+ assertThat(keyboardQuickSwitchController.isShown).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testHotseatItemTasksNotShownInRecents() {
+ val maxNumIconViews = maxNumberOfTaskbarIcons
+ // Assume there are at least all apps and divider icon, as they would appear once running
+ // apps are added, even if not present initially.
+ val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
+ val hotseatItems = createHotseatItems(1)
+
+ val targetOverflowSize = 5
+ val createdTasks = maxNumIconViews - initialIconCount + targetOverflowSize
+ createDesktopTaskWithTasksFromPackages(
+ listOf("fake") +
+ listOf(hotseatItems[0]?.targetPackage ?: "") +
+ List(createdTasks - 2) { "fake" }
+ )
+
+ runOnMainSync {
+ val taskbarView: TaskbarView =
+ taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
+ taskbarView.updateItems(
+ recentAppsController.updateHotseatItemInfos(hotseatItems as Array<ItemInfo?>),
+ recentAppsController.shownTasks,
+ )
+ }
+
+ assertThat(maxNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
+ assertThat(currentNumberOfTaskbarIcons).isEqualTo(maxNumIconViews)
+ assertThat(taskbarOverflowIconIndex).isEqualTo(initialIconCount + hotseatItems.size)
+ assertThat(overflowItems)
+ .containsExactlyElementsIn(listOf(0) + (2..targetOverflowSize + 1).toList())
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testHotseatItemTasksNotShownInKQS() {
+ val maxNumIconViews = maxNumberOfTaskbarIcons
+ // Assume there are at least all apps and divider icon, as they would appear once running
+ // apps are added, even if not present initially.
+ val initialIconCount = currentNumberOfTaskbarIcons.coerceAtLeast(2)
+ val hotseatItems = createHotseatItems(1)
+
+ val targetOverflowSize = 5
+ val createdTasks = maxNumIconViews - initialIconCount + targetOverflowSize
+ createDesktopTaskWithTasksFromPackages(
+ listOf("fake") +
+ listOf(hotseatItems[0]?.targetPackage ?: "") +
+ List(createdTasks - 2) { "fake" }
+ )
+
+ runOnMainSync {
+ val taskbarView: TaskbarView =
+ taskbarUnitTestRule.activityContext.dragLayer.findViewById(R.id.taskbar_view)
+ taskbarView.updateItems(
+ recentAppsController.updateHotseatItemInfos(hotseatItems as Array<ItemInfo?>),
+ recentAppsController.shownTasks,
+ )
+ }
+
+ tapOverflowIcon()
+ // Keyboard quick switch view is shown only after list of recent task is asynchronously
+ // retrieved from the recents model.
+ runOnMainSync { recentsModel.resolvePendingTaskRequests() }
+
+ assertThat(getOnUiThread { keyboardQuickSwitchController.isShownFromTaskbar }).isTrue()
+ assertThat(getOnUiThread { keyboardQuickSwitchController.shownTaskIds() })
+ .containsExactlyElementsIn(listOf(0) + (2..<createdTasks).toList())
+ }
+
private fun createDesktopTask(tasksToAdd: Int) {
+ createDesktopTaskWithTasksFromPackages((0..<tasksToAdd).map { "fake" })
+ }
+
+ private fun createDesktopTaskWithTasksFromPackages(packages: List<String>) {
val tasks =
- (0..<tasksToAdd).map {
- Task(Task.TaskKey(it, 0, Intent(), ComponentName("", ""), 0, 2000))
- }
+ packages.mapIndexed({ index, p ->
+ Task(
+ Task.TaskKey(
+ index,
+ 0,
+ Intent().apply { `package` = p },
+ ComponentName(p, ""),
+ Process.myUserHandle().identifier,
+ 2000,
+ )
+ )
+ })
+
recentsModel.updateRecentTasks(listOf(DesktopTask(deskId = 0, tasks)))
- for (task in 1..tasksToAdd) {
+ for (task in 1..tasks.size) {
desktopTaskListener?.onTasksVisibilityChanged(
context.virtualDisplay.display.displayId,
task,
@@ -394,6 +508,14 @@
}
}
+ private fun tapOverflowIcon() {
+ runOnMainSync {
+ val overflowIcon =
+ taskbarViewController.iconViews.firstOrNull { it is TaskbarOverflowView }
+ assertThat(overflowIcon?.callOnClick()).isTrue()
+ }
+ }
+
/**
* Adds enough running apps for taskbar to enter overflow of `targetOverflowSize`, and verifies
* * max number of icons in the taskbar remains unchanged
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt
index a7bfa9a..5f7b360 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/MockedRecentsModelHelper.kt
@@ -19,6 +19,7 @@
import com.android.quickstep.RecentsModel
import com.android.quickstep.RecentsModel.RecentTasksChangedListener
import com.android.quickstep.TaskIconCache
+import com.android.quickstep.TaskThumbnailCache
import com.android.quickstep.util.GroupTask
import java.util.function.Consumer
import org.mockito.kotlin.any
@@ -27,9 +28,11 @@
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
-/** Helper class to mock the {@link RecentsModel} object in test */
+/** Helper class to mock the [RecentsModel] object in test */
class MockedRecentsModelHelper {
private val mockIconCache: TaskIconCache = mock()
+ private val mockThumbnailCache: TaskThumbnailCache = mock()
+
var taskListId = 0
var recentTasksChangedListener: RecentTasksChangedListener? = null
var taskRequests: MutableList<(List<GroupTask>) -> Unit> = mutableListOf()
@@ -37,6 +40,8 @@
val mockRecentsModel: RecentsModel = mock {
on { iconCache } doReturn mockIconCache
+ on { thumbnailCache } doReturn mockThumbnailCache
+
on { unregisterRecentTasksChangedListener() } doAnswer { recentTasksChangedListener = null }
on { registerRecentTasksChangedListener(any<RecentTasksChangedListener>()) } doAnswer