Fix blank recent tasks thumbnails.
When user navigate back to Overview, getAllTaskData will be called and refresh the list of tasks in the repository which cleans the pre-loaded thumbnails and icons from the previous iteration.
In some cases, this causes the thumbnails to be blank in the recents. When getAllTaskData is called after the taskRequest is completed, the tasks will end up in an invalid state without the thumbnail and icon until the user scrolls through the recent apps list or re-open Overview.
Fix: 380067701
Flag: com.android.launcher3.enable_refactor_task_thumbnail
Test: TasksRepositoryTest
Change-Id: Ia15d905a2f5ceb71b87c0b91028ce88894524806
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 8c26d8f..a315775 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -51,17 +51,32 @@
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
if (forceRefresh) {
recentsModel.getTasks { result ->
- tasks.value =
- MapForStateFlow(
- result
- .flatMap { groupTask -> groupTask.tasks }
- .associateBy { it.key.id }
- .also {
- // Clean tasks that are not in the latest group tasks list.
- val tasksNoLongerVisible = it.keys.subtract(tasks.value.keys)
- removeTasks(tasksNoLongerVisible)
+ val recentTasks =
+ result
+ .flatMap { groupTask -> groupTask.tasks }
+ .associateBy { it.key.id }
+ .also { hashMap ->
+ // Clean tasks that are not in the latest group tasks list.
+ val tasksNoLongerVisible = hashMap.keys.subtract(tasks.value.keys)
+ removeTasks(tasksNoLongerVisible)
+
+ // Use pre-loaded thumbnail data and icon from the previous list.
+ // This reduces the Thumbnail loading time in the Overview and prevent
+ // empty thumbnail and icon.
+ val cache =
+ taskRequests.keys
+ .mapNotNull { key ->
+ val task = tasks.value[key] ?: return@mapNotNull null
+ key to Pair(task.thumbnail, task.icon)
+ }
+ .toMap()
+
+ hashMap.values.forEach { task ->
+ task.thumbnail = task.thumbnail ?: cache[task.key.id]?.first
+ task.icon = task.icon ?: cache[task.key.id]?.second
}
- )
+ }
+ tasks.value = MapForStateFlow(recentTasks)
}
}
return tasks.map { it.values.toList() }
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
index eaeb513..9e99a0b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
@@ -20,10 +20,12 @@
import java.util.function.Consumer
class FakeRecentTasksDataSource : RecentTasksDataSource {
- var taskList: List<GroupTask> = listOf()
+ private var taskList: List<GroupTask> = listOf()
override fun getTasks(callback: Consumer<List<GroupTask>>?): Int {
- callback?.accept(taskList)
+ // Makes a copy of the GroupTask to create a new GroupTask instance and to simulate
+ // RecentsModel::getTasks behavior.
+ callback?.accept(taskList.map { it.copy() })
return 0
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index e0e7f28..624310b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -117,6 +117,20 @@
}
@Test
+ fun whenThumbnailIsLoaded_getAllTaskData_usesPreviousLoadedThumbnailAndIcon() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+ val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
+
+ systemUnderTest.setVisibleTasks(setOf(1))
+ assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
+
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+ assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
+ }
+
+ @Test
fun getCurrentThumbnailByIdReturnsThumbnailWithLoadedThumbnails() =
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)