Merge "Avoid per task app icons in Taskbar for desktop mode" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 2745129..6db6233 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -85,6 +85,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
@@ -1342,7 +1343,7 @@
mControllers.uiController.onTaskbarIconLaunched(api);
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
}
- } else if (tag instanceof TaskItemInfo info) {
+ } else if (tag instanceof TaskItemInfo info && !Flags.enableMultiInstanceMenuTaskbar()) {
RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId())
? createUnminimizeRemoteTransition() : null;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index a059b22..e654fe8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -19,6 +19,7 @@
import android.window.DesktopModeFlags
import androidx.annotation.VisibleForTesting
import com.android.launcher3.BubbleTextView.RunningAppState
+import com.android.launcher3.Flags
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
@@ -276,27 +277,60 @@
return newOrder
}
+ /**
+ * Computes the list of running tasks to be shown in the recent apps section of the taskbar in
+ * desktop mode, taking into account deduplication against hotseat items and existing tasks.
+ */
private fun computeShownRunningTasks(): List<GroupTask> {
if (!canShowRunningApps) {
return emptyList()
}
- val desktopTaskAsList = getOrderedAndWrappedDesktopTasks()
- val desktopTaskIds = desktopTaskAsList.map { it.task1.key.id }
- val shownTaskIds = shownTasks.map { it.task1.key.id }
- // TODO(b/315344726 Multi-instance support): only show one icon per package once we support
- // taskbar multi-instance menus
- val shownHotseatItemTaskIds =
- shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId }
- // Remove any newly-missing Tasks, and actual group-tasks
+
+ val desktopTasks = getOrderedAndWrappedDesktopTasks()
+
val newShownTasks =
- shownTasks
- .filter { !it.supportsMultipleTasks() }
- .filter { it.task1.key.id in desktopTaskIds }
- .toMutableList()
- // Add any new Tasks, maintaining the order from previous shownTasks.
- newShownTasks.addAll(desktopTaskAsList.filter { it.task1.key.id !in shownTaskIds })
- // Remove any tasks already covered by Hotseat icons
- return newShownTasks.filter { it.task1.key.id !in shownHotseatItemTaskIds }
+ if (Flags.enableMultiInstanceMenuTaskbar()) {
+ val deduplicatedDesktopTasks =
+ desktopTasks.distinctBy { Pair(it.task1.key.packageName, it.task1.key.userId) }
+
+ shownTasks
+ .filter {
+ !it.supportsMultipleTasks() &&
+ it.task1.key.id in deduplicatedDesktopTasks.map { it.task1.key.id }
+ }
+ .toMutableList()
+ .apply {
+ addAll(
+ deduplicatedDesktopTasks.filter { currentTask ->
+ val currentTaskKey = currentTask.task1.key
+ currentTaskKey.id !in shownTasks.map { it.task1.key.id } &&
+ shownHotseatItems.none { hotseatItem ->
+ hotseatItem.targetPackage == currentTaskKey.packageName &&
+ hotseatItem.user.identifier == currentTaskKey.userId
+ }
+ }
+ )
+ }
+ } else {
+ val desktopTaskIds = desktopTasks.map { it.task1.key.id }
+ val shownHotseatItemTaskIds =
+ shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId }
+
+ shownTasks
+ .filter { !it.supportsMultipleTasks() && it.task1.key.id in desktopTaskIds }
+ .toMutableList()
+ .apply {
+ addAll(
+ desktopTasks.filter { desktopTask ->
+ desktopTask.task1.key.id !in
+ shownTasks.map { shownTask -> shownTask.task1.key.id }
+ }
+ )
+ removeAll { it.task1.key.id in shownHotseatItemTaskIds }
+ }
+ }
+
+ return newShownTasks
}
private fun computeShownRecentTasks(): List<GroupTask> {
@@ -305,7 +339,6 @@
}
// Remove the current task.
val allRecentTasks = allRecentTasks.subList(0, allRecentTasks.size - 1)
- // TODO(b/315344726 Multi-instance support): dedupe Tasks of the same package too
var shownTasks = dedupeHotseatTasks(allRecentTasks, shownHotseatItems)
if (shownTasks.size > MAX_RECENT_TASKS) {
// Remove any tasks older than MAX_RECENT_TASKS.
@@ -318,10 +351,22 @@
groupTasks: List<GroupTask>,
shownHotseatItems: List<ItemInfo>,
): List<GroupTask> {
- val hotseatPackages = shownHotseatItems.map { item -> item.targetPackage }
- return groupTasks.filter { groupTask ->
- groupTask.hasMultipleTasks() ||
- !hotseatPackages.contains(groupTask.task1.key.packageName)
+ return if (Flags.enableMultiInstanceMenuTaskbar()) {
+ groupTasks.filter { groupTask ->
+ val taskKey = groupTask.task1.key
+ // Keep tasks that are group tasks or unique package name/user combinations
+ groupTask.hasMultipleTasks() ||
+ shownHotseatItems.none {
+ it.targetPackage == taskKey.packageName &&
+ it.user.identifier == taskKey.userId
+ }
+ }
+ } else {
+ val hotseatPackages = shownHotseatItems.map { it.targetPackage }
+ groupTasks.filter { groupTask ->
+ groupTask.hasMultipleTasks() ||
+ !hotseatPackages.contains(groupTask.task1.key.packageName)
+ }
}
}
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 13880f1..9ca8a1b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -19,9 +19,11 @@
import android.animation.AnimatorTestRule
import android.content.ComponentName
import android.content.Intent
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR
import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
@@ -64,6 +66,7 @@
FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
FLAG_ENABLE_BUBBLE_BAR,
)
+@DisableFlags(FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR)
class TaskbarOverflowTest {
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index ed0c928..fbfc40b 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -23,10 +23,12 @@
import android.content.res.Resources
import android.os.Process
import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
import android.platform.test.rule.TestWatcher
import android.testing.AndroidTestingRunner
import com.android.internal.R
import com.android.launcher3.BubbleTextView.RunningAppState
+import com.android.launcher3.Flags
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
import com.android.launcher3.model.data.AppInfo
@@ -57,6 +59,7 @@
import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
+@EnableFlags(Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR)
class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
@get:Rule val mockitoRule = MockitoJUnit.rule()
@@ -323,8 +326,12 @@
assertThat(hotseatItem1.taskId).isEqualTo(1)
}
+ /**
+ * Tests that in desktop mode, when two tasks have the same package name and one is in the
+ * hotseat, only the hotseat item represents the app, and no duplicate is shown in recent apps.
+ */
@Test
- fun updateHotseatItemInfos_inDesktopMode_twoRunningTasksSamePackage_hotseatCoversFirstTask() {
+ fun updateHotseatItemInfos_inDesktopMode_twoRunningTasksSamePackage_onlyHotseatCoversTask() {
setInDesktopMode(true)
val newHotseatItems =
@@ -338,16 +345,16 @@
recentTaskPackages = emptyList(),
)
- // First task is in Hotseat Items
+ // The task is in Hotseat Items
assertThat(newHotseatItems).hasLength(2)
assertThat(newHotseatItems[0]).isInstanceOf(TaskItemInfo::class.java)
assertThat(newHotseatItems[1]).isNotInstanceOf(TaskItemInfo::class.java)
val hotseatItem1 = newHotseatItems[0] as TaskItemInfo
- assertThat(hotseatItem1.taskId).isEqualTo(1)
- // Second task is in shownTasks
+ assertThat(hotseatItem1.targetPackage).isEqualTo(HOTSEAT_PACKAGE_1)
+
+ // The other task of the same package is not in shownTasks
val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks)
- .containsExactlyElementsIn(listOf(createTask(id = 2, HOTSEAT_PACKAGE_1)))
+ assertThat(shownTasks).isEmpty()
}
@Test
@@ -530,8 +537,12 @@
assertThat(shownTasks).isEqualTo(listOf(task1, task2))
}
+ /**
+ * Tests that when multiple instances of the same app are running in desktop mode and the app is
+ * not in the hotseat, only one instance is shown in the recent apps section.
+ */
@Test
- fun onRecentTasksChanged_inDesktopMode_multiInstance_shownTasks_maintainsOrder() {
+ fun onRecentTasksChanged_inDesktopMode_multiInstance_noHotseat_shownTasksHasOneInstance() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_1)
@@ -541,43 +552,10 @@
recentTaskPackages = emptyList(),
)
- prepareHotseatAndRunningAndRecentApps(
- hotseatPackages = emptyList(),
- runningTasks = listOf(task2, task1),
- recentTaskPackages = emptyList(),
- )
-
+ // Assert that shownTasks contains only one instance of the app
val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).isEqualTo(listOf(task1, task2))
- }
-
- @Test
- fun updateHotseatItems_inDesktopMode_multiInstanceHotseatPackage_shownItems_maintainsOrder() {
- setInDesktopMode(true)
- val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
- val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_1)
- prepareHotseatAndRunningAndRecentApps(
- hotseatPackages = listOf(RUNNING_APP_PACKAGE_1),
- runningTasks = listOf(task1, task2),
- recentTaskPackages = emptyList(),
- )
- updateRecentTasks( // Trigger a recent-tasks change before calling updateHotseatItems()
- runningTasks = listOf(task2, task1),
- recentTaskPackages = emptyList(),
- )
-
- prepareHotseatAndRunningAndRecentApps(
- hotseatPackages = listOf(RUNNING_APP_PACKAGE_1),
- runningTasks = listOf(task2, task1),
- recentTaskPackages = emptyList(),
- )
-
- val newHotseatItems = recentAppsController.shownHotseatItems
- assertThat(newHotseatItems).hasSize(1)
- assertThat(newHotseatItems[0]).isInstanceOf(TaskItemInfo::class.java)
- assertThat((newHotseatItems[0] as TaskItemInfo).taskId).isEqualTo(1)
- val shownTasks = recentAppsController.shownTasks.map { it.task1 }
- assertThat(shownTasks).isEqualTo(listOf(task2))
+ assertThat(shownTasks).hasSize(1)
+ assertThat(shownTasks[0].key.packageName).isEqualTo(RUNNING_APP_PACKAGE_1)
}
@Test