Files
Lawnchair/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
T
Gustav Sennton f3108c59ed Taskbar running apps: show one icon per task + tap opens that task
Before this CL we would show only one Taskbar icon per app, making it
impossible (through the taskbar) to switch between several windows of
the same app.
With this CL we add one icon per task instead, making it possible to
bring each task to the front individually.

Flag: com.android.window.flags.enable_desktop_windowing_taskbar_running_apps
Bug: 351118893
Bug: 349790805
Bug: 351156858
Test: Started several Chrome windows -> taskbar has one icon per window

Change-Id: Ia692977effceb9ce339906bf6ca24d73e19d8769
2024-07-09 14:48:04 +00:00

782 lines
33 KiB
Kotlin

/*
* Copyright (C) 2024 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.launcher3.taskbar
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.ComponentName
import android.content.Intent
import android.os.Process
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.quickstep.RecentsModel
import com.android.quickstep.RecentsModel.RecentTasksChangedListener
import com.android.quickstep.TaskIconCache
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.systemui.shared.recents.model.Task
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
@get:Rule val mockitoRule = MockitoJUnit.rule()
@Mock private lateinit var mockIconCache: TaskIconCache
@Mock private lateinit var mockRecentsModel: RecentsModel
@Mock private lateinit var mockDesktopVisibilityController: DesktopVisibilityController
private var taskListChangeId: Int = 1
private lateinit var recentAppsController: TaskbarRecentAppsController
private lateinit var recentTasksChangedListener: RecentTasksChangedListener
private lateinit var userHandle: UserHandle
@Before
fun setUp() {
super.setup()
userHandle = Process.myUserHandle()
whenever(mockRecentsModel.iconCache).thenReturn(mockIconCache)
recentAppsController =
TaskbarRecentAppsController(mockRecentsModel) { mockDesktopVisibilityController }
recentAppsController.init(taskbarControllers)
recentAppsController.canShowRunningApps = true
recentAppsController.canShowRecentApps = true
val listenerCaptor = ArgumentCaptor.forClass(RecentTasksChangedListener::class.java)
verify(mockRecentsModel).registerRecentTasksChangedListener(listenerCaptor.capture())
recentTasksChangedListener = listenerCaptor.value
// Make sure updateHotseatItemInfos() is called after commitRunningAppsToUI()
whenever(taskbarViewController.commitRunningAppsToUI()).then {
recentAppsController.updateHotseatItemInfos(
recentAppsController.shownHotseatItems.toTypedArray()
)
}
}
@Test
fun updateHotseatItemInfos_cantShowRunning_inDesktopMode_returnsAllHotseatItems() {
recentAppsController.canShowRunningApps = false
setInDesktopMode(true)
val hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1)
val newHotseatItems =
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = hotseatPackages,
runningTasks = emptyList(),
recentTaskPackages = emptyList()
)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(hotseatPackages)
}
@Test
fun updateHotseatItemInfos_cantShowRecent_notInDesktopMode_returnsAllHotseatItems() {
recentAppsController.canShowRecentApps = false
setInDesktopMode(false)
val hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1)
val newHotseatItems =
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = hotseatPackages,
runningTasks = emptyList(),
recentTaskPackages = emptyList()
)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(hotseatPackages)
}
@Test
fun updateHotseatItemInfos_canShowRunning_inDesktopMode_returnsNonPredictedHotseatItems() {
recentAppsController.canShowRunningApps = true
setInDesktopMode(true)
val newHotseatItems =
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1),
runningTasks = emptyList(),
recentTaskPackages = emptyList()
)
val expectedPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(expectedPackages)
}
@Test
fun updateHotseatItemInfos_inDesktopMode_hotseatPackageHasRunningTask_hotseatItemLinksToTask() {
setInDesktopMode(true)
val newHotseatItems =
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
runningTasks = listOf(createTask(id = 1, HOTSEAT_PACKAGE_1)),
recentTaskPackages = emptyList()
)
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)
}
@Test
fun updateHotseatItemInfos_inDesktopMode_twoRunningTasksSamePackage_hotseatCoversFirstTask() {
setInDesktopMode(true)
val newHotseatItems =
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
runningTasks =
listOf(
createTask(id = 1, HOTSEAT_PACKAGE_1),
createTask(id = 2, HOTSEAT_PACKAGE_1)
),
recentTaskPackages = emptyList()
)
// First 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
val shownTasks = recentAppsController.shownTasks.map { it.task1 }
assertThat(shownTasks)
.containsExactlyElementsIn(listOf(createTask(id = 2, HOTSEAT_PACKAGE_1)))
}
@Test
fun updateHotseatItemInfos_canShowRecent_notInDesktopMode_returnsNonPredictedHotseatItems() {
recentAppsController.canShowRecentApps = true
setInDesktopMode(false)
val newHotseatItems =
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1),
runningTasks = emptyList(),
recentTaskPackages = emptyList()
)
val expectedPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2)
assertThat(newHotseatItems.map { it?.targetPackage })
.containsExactlyElementsIn(expectedPackages)
}
@Test
fun onRecentTasksChanged_cantShowRunning_inDesktopMode_shownTasks_returnsEmptyList() {
recentAppsController.canShowRunningApps = false
setInDesktopMode(true)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1),
runningTasks =
listOf(
createTask(id = 1, RUNNING_APP_PACKAGE_1),
createTask(id = 2, RUNNING_APP_PACKAGE_2)
),
recentTaskPackages = emptyList()
)
assertThat(recentAppsController.shownTasks).isEmpty()
}
@Test
fun onRecentTasksChanged_cantShowRecent_notInDesktopMode_shownTasks_returnsEmptyList() {
recentAppsController.canShowRecentApps = false
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2, PREDICTED_PACKAGE_1),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
assertThat(recentAppsController.shownTasks).isEmpty()
}
@Test
fun onRecentTasksChanged_notInDesktopMode_noRecentTasks_shownTasks_returnsEmptyList() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks =
listOf(
createTask(id = 1, RUNNING_APP_PACKAGE_1),
createTask(id = 2, RUNNING_APP_PACKAGE_2)
),
recentTaskPackages = emptyList()
)
assertThat(recentAppsController.shownTasks).isEmpty()
assertThat(recentAppsController.minimizedTaskIds).isEmpty()
}
@Test
fun onRecentTasksChanged_inDesktopMode_noRunningApps_shownTasks_returnsEmptyList() {
setInDesktopMode(true)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
assertThat(recentAppsController.shownTasks).isEmpty()
}
@Test
fun onRecentTasksChanged_inDesktopMode_shownTasks_returnsRunningTasks() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
val shownTasks = recentAppsController.shownTasks.map { it.task1 }
assertThat(shownTasks).containsExactlyElementsIn(listOf(task1, task2))
}
@Test
fun onRecentTasksChanged_notInDesktopMode_getRunningApps_returnsEmptySet() {
setInDesktopMode(false)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
assertThat(recentAppsController.runningTaskIds).isEmpty()
assertThat(recentAppsController.minimizedTaskIds).isEmpty()
}
@Test
fun onRecentTasksChanged_inDesktopMode_getRunningApps_returnsAllDesktopTasks() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
assertThat(recentAppsController.runningTaskIds).containsExactlyElementsIn(listOf(1, 2))
assertThat(recentAppsController.minimizedTaskIds).isEmpty()
}
@Test
fun onRecentTasksChanged_inDesktopMode_getRunningApps_includesHotseat() {
setInDesktopMode(true)
val runningTasks =
listOf(
createTask(id = 1, HOTSEAT_PACKAGE_1),
createTask(id = 2, RUNNING_APP_PACKAGE_1),
createTask(id = 3, RUNNING_APP_PACKAGE_2)
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
runningTasks = runningTasks,
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
assertThat(recentAppsController.runningTaskIds).containsExactlyElementsIn(listOf(1, 2, 3))
assertThat(recentAppsController.minimizedTaskIds).isEmpty()
}
@Test
fun onRecentTasksChanged_inDesktopMode_allAppsRunningAndInvisibleAppsMinimized() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
val task3Minimized = createTask(id = 3, RUNNING_APP_PACKAGE_3, isVisible = false)
val runningTasks = listOf(task1, task2, task3Minimized)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = runningTasks,
recentTaskPackages = emptyList()
)
assertThat(recentAppsController.runningTaskIds).containsExactly(1, 2, 3)
assertThat(recentAppsController.minimizedTaskIds).containsExactly(3)
}
@Test
fun onRecentTasksChanged_inDesktopMode_samePackage_differentTasks_severalRunningTasks() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
assertThat(recentAppsController.runningTaskIds).containsExactlyElementsIn(listOf(1, 2))
}
@Test
fun onRecentTasksChanged_inDesktopMode_shownTasks_maintainsOrder() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task2, task1),
recentTaskPackages = emptyList()
)
val shownTasks = recentAppsController.shownTasks.map { it.task1 }
assertThat(shownTasks).isEqualTo(listOf(task1, task2))
}
@Test
fun onRecentTasksChanged_inDesktopMode_multiInstance_shownTasks_maintainsOrder() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_1)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task2, task1),
recentTaskPackages = emptyList()
)
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))
}
@Test
fun onRecentTasksChanged_notInDesktopMode_shownTasks_maintainsRecency() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2, RECENT_PACKAGE_3)
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_2, RECENT_PACKAGE_3, RECENT_PACKAGE_1)
)
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
// Most recent packages, minus the currently running one (RECENT_PACKAGE_1).
assertThat(shownPackages).isEqualTo(listOf(RECENT_PACKAGE_2, RECENT_PACKAGE_3))
}
@Test
fun onRecentTasksChanged_inDesktopMode_addTask_shownTasks_maintainsOrder() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
val task3 = createTask(id = 3, RUNNING_APP_PACKAGE_3)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2),
recentTaskPackages = emptyList()
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task2, task1, task3),
recentTaskPackages = emptyList()
)
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
val expectedOrder =
listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
assertThat(shownPackages).isEqualTo(expectedOrder)
}
@Test
fun onRecentTasksChanged_notInDesktopMode_addTask_shownTasks_maintainsRecency() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_3, RECENT_PACKAGE_2)
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_2, RECENT_PACKAGE_3, RECENT_PACKAGE_1)
)
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
// Most recent packages, minus the currently running one (RECENT_PACKAGE_1).
assertThat(shownPackages).isEqualTo(listOf(RECENT_PACKAGE_2, RECENT_PACKAGE_3))
}
@Test
fun onRecentTasksChanged_inDesktopMode_removeTask_shownTasks_maintainsOrder() {
setInDesktopMode(true)
val task1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val task2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
val task3 = createTask(id = 3, RUNNING_APP_PACKAGE_3)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1, task2, task3),
recentTaskPackages = emptyList()
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task2, task1),
recentTaskPackages = emptyList()
)
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
assertThat(shownPackages).isEqualTo(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
}
@Test
fun onRecentTasksChanged_notInDesktopMode_removeTask_shownTasks_maintainsRecency() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2, RECENT_PACKAGE_3)
)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_2, RECENT_PACKAGE_3)
)
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
// Most recent packages, minus the currently running one (RECENT_PACKAGE_3).
assertThat(shownPackages).isEqualTo(listOf(RECENT_PACKAGE_2))
}
@Test
fun onRecentTasksChanged_enterDesktopMode_shownTasks_onlyIncludesRunningTasks() {
setInDesktopMode(false)
val runningTask1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val runningTask2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
val recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(runningTask1, runningTask2),
recentTaskPackages = recentTaskPackages
)
setInDesktopMode(true)
recentTasksChangedListener.onRecentTasksChanged()
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
assertThat(shownPackages).containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
}
@Test
fun onRecentTasksChanged_exitDesktopMode_shownTasks_onlyIncludesRecentTasks() {
setInDesktopMode(true)
val runningTask1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val runningTask2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
val recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2, RECENT_PACKAGE_3)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(runningTask1, runningTask2),
recentTaskPackages = recentTaskPackages
)
setInDesktopMode(false)
recentTasksChangedListener.onRecentTasksChanged()
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
// Don't expect RECENT_PACKAGE_3 because it is currently running.
val expectedPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
assertThat(shownPackages).containsExactlyElementsIn(expectedPackages)
}
@Test
fun onRecentTasksChanged_notInDesktopMode_hasRecentTasks_shownTasks_returnsRecentTasks() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2, RECENT_PACKAGE_3)
)
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
// RECENT_PACKAGE_3 is the top task (visible to user) so should be excluded.
val expectedPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
assertThat(shownPackages).containsExactlyElementsIn(expectedPackages)
}
@Test
fun onRecentTasksChanged_notInDesktopMode_hasRecentAndRunningTasks_shownTasks_returnsRecentTaskAndDesktopTile() {
setInDesktopMode(false)
val runningTask1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val runningTask2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(runningTask1, runningTask2),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
val shownPackages = recentAppsController.shownTasks.map { it.packageNames }
// Only 2 recent tasks shown: Desktop Tile + 1 Recent Task
val desktopTilePackages = listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
val recentTaskPackages = listOf(RECENT_PACKAGE_1)
val expectedPackages = listOf(desktopTilePackages, recentTaskPackages)
assertThat(shownPackages).containsExactlyElementsIn(expectedPackages)
}
@Test
fun onRecentTasksChanged_notInDesktopMode_hasRecentAndSplitTasks_shownTasks_returnsRecentTaskAndPair() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_SPLIT_PACKAGES_1, RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
val shownPackages = recentAppsController.shownTasks.map { it.packageNames }
// Only 2 recent tasks shown: Pair + 1 Recent Task
val pairPackages = RECENT_SPLIT_PACKAGES_1.split("_")
val recentTaskPackages = listOf(RECENT_PACKAGE_1)
val expectedPackages = listOf(pairPackages, recentTaskPackages)
assertThat(shownPackages).containsExactlyElementsIn(expectedPackages)
}
@Test
fun onRecentTasksChanged_notInDesktopMode_noActualChangeToRecents_commitRunningAppsToUI_notCalled() {
setInDesktopMode(false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
verify(taskbarViewController, times(1)).commitRunningAppsToUI()
// Call onRecentTasksChanged() again with the same tasks, verify it's a no-op.
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = emptyList(),
recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
)
verify(taskbarViewController, times(1)).commitRunningAppsToUI()
}
@Test
fun onRecentTasksChanged_inDesktopMode_noActualChangeToRunning_commitRunningAppsToUI_notCalled() {
setInDesktopMode(true)
val runningTask1 = createTask(id = 1, RUNNING_APP_PACKAGE_1)
val runningTask2 = createTask(id = 2, RUNNING_APP_PACKAGE_2)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(runningTask1, runningTask2),
recentTaskPackages = emptyList()
)
verify(taskbarViewController, times(1)).commitRunningAppsToUI()
// Call onRecentTasksChanged() again with the same tasks, verify it's a no-op.
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(runningTask1, runningTask2),
recentTaskPackages = emptyList()
)
verify(taskbarViewController, times(1)).commitRunningAppsToUI()
}
@Test
fun onRecentTasksChanged_onlyMinimizedChanges_commitRunningAppsToUI_isCalled() {
setInDesktopMode(true)
val task1Minimized = createTask(id = 1, RUNNING_APP_PACKAGE_1, isVisible = false)
val task2Visible = createTask(id = 2, RUNNING_APP_PACKAGE_2)
val task2Minimized = createTask(id = 2, RUNNING_APP_PACKAGE_2, isVisible = false)
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1Minimized, task2Visible),
recentTaskPackages = emptyList()
)
verify(taskbarViewController, times(1)).commitRunningAppsToUI()
// Call onRecentTasksChanged() again with a new minimized app, verify we update UI.
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = emptyList(),
runningTasks = listOf(task1Minimized, task2Minimized),
recentTaskPackages = emptyList()
)
verify(taskbarViewController, times(2)).commitRunningAppsToUI()
}
@Test
fun onRecentTasksChanged_hotseatAppStartsRunning_commitRunningAppsToUI_isCalled() {
setInDesktopMode(true)
val hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2)
val originalTasks = listOf(createTask(id = 1, RUNNING_APP_PACKAGE_1))
val newTasks =
listOf(createTask(id = 1, RUNNING_APP_PACKAGE_1), createTask(id = 2, HOTSEAT_PACKAGE_1))
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = hotseatPackages,
runningTasks = originalTasks,
recentTaskPackages = emptyList()
)
verify(taskbarViewController, times(1)).commitRunningAppsToUI()
// Call onRecentTasksChanged() again with a new running app, verify we update UI.
prepareHotseatAndRunningAndRecentApps(
hotseatPackages = hotseatPackages,
runningTasks = newTasks,
recentTaskPackages = emptyList()
)
verify(taskbarViewController, times(2)).commitRunningAppsToUI()
}
private fun prepareHotseatAndRunningAndRecentApps(
hotseatPackages: List<String>,
runningTasks: List<Task>,
recentTaskPackages: List<String>,
): Array<ItemInfo?> {
val hotseatItems = createHotseatItemsFromPackageNames(hotseatPackages)
recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
updateRecentTasks(runningTasks, recentTaskPackages)
return recentAppsController.shownHotseatItems.toTypedArray()
}
private fun updateRecentTasks(
runningTasks: List<Task>,
recentTaskPackages: List<String>,
) {
val recentTasks = createRecentTasksFromPackageNames(recentTaskPackages)
val allTasks =
ArrayList<GroupTask>().apply {
if (!runningTasks.isEmpty()) {
add(DesktopTask(ArrayList(runningTasks)))
}
addAll(recentTasks)
}
doAnswer {
val callback: Consumer<ArrayList<GroupTask>> = it.getArgument(0)
callback.accept(allTasks)
taskListChangeId
}
.whenever(mockRecentsModel)
.getTasks(any<Consumer<List<GroupTask>>>())
recentTasksChangedListener.onRecentTasksChanged()
}
private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {
return packageNames
.map {
createTestAppInfo(packageName = it).apply {
container =
if (it.startsWith("predicted")) {
CONTAINER_HOTSEAT_PREDICTION
} else {
CONTAINER_HOTSEAT
}
}
}
.map { it.makeWorkspaceItem(taskbarActivityContext) }
}
private fun createTestAppInfo(
packageName: String = "testPackageName",
className: String = "testClassName"
) = AppInfo(ComponentName(packageName, className), className /* title */, userHandle, Intent())
private fun createRecentTasksFromPackageNames(packageNames: List<String>): List<GroupTask> {
return packageNames.map { packageName ->
if (packageName.startsWith("split")) {
val splitPackages = packageName.split("_")
GroupTask(
createTask(100, splitPackages[0]),
createTask(101, splitPackages[1]),
/* splitBounds = */ null
)
} else {
// Use the number at the end of the test packageName as the id.
val id = 1000 + packageName[packageName.length - 1].code
GroupTask(createTask(id, packageName))
}
}
}
private fun createTask(id: Int, packageName: String, isVisible: Boolean = true): Task {
return Task(
Task.TaskKey(
id,
WINDOWING_MODE_FREEFORM,
Intent().apply { `package` = packageName },
ComponentName(packageName, "TestActivity"),
userHandle.identifier,
0
)
)
.apply { this.isVisible = isVisible }
}
private fun setInDesktopMode(inDesktopMode: Boolean) {
whenever(mockDesktopVisibilityController.areDesktopTasksVisible()).thenReturn(inDesktopMode)
}
private val GroupTask.packageNames: List<String>
get() = tasks.map { task -> task.key.packageName }
private companion object {
const val HOTSEAT_PACKAGE_1 = "hotseat1"
const val HOTSEAT_PACKAGE_2 = "hotseat2"
const val PREDICTED_PACKAGE_1 = "predicted1"
const val RUNNING_APP_PACKAGE_1 = "running1"
const val RUNNING_APP_PACKAGE_2 = "running2"
const val RUNNING_APP_PACKAGE_3 = "running3"
const val RECENT_PACKAGE_1 = "recent1"
const val RECENT_PACKAGE_2 = "recent2"
const val RECENT_PACKAGE_3 = "recent3"
const val RECENT_SPLIT_PACKAGES_1 = "split1_split2"
}
}