Merge changes I54ea7a71,Ifd9c54fd into main
* changes:
Set task properties to prevent the task being null. This behaviour is expected by existing callers and was likely broken by ag/28151977
TaskRepository performance improvement
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 91fa72d..c4221a1 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -116,6 +116,10 @@
() -> getCacheEntry(task),
MAIN_EXECUTOR,
result -> {
+ task.icon = result.icon;
+ task.titleDescription = result.contentDescription;
+ task.title = result.title;
+
callback.onTaskIconReceived(
result.icon,
result.contentDescription,
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
index 9c4248c..3b59864 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
@@ -40,5 +40,5 @@
* Sets the tasks that are visible, indicating that properties relating to visuals need to be
* populated e.g. icons/thumbnails etc.
*/
- fun setVisibleTasks(visibleTaskIdList: List<Int>)
+ fun setVisibleTasks(visibleTaskIdList: Set<Int>)
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index dc8d537..4f38ec7 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -23,147 +23,147 @@
import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
-import com.android.quickstep.util.GroupTask
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
-@OptIn(ExperimentalCoroutinesApi::class)
class TasksRepository(
private val recentsModel: RecentTasksDataSource,
private val taskThumbnailDataSource: TaskThumbnailDataSource,
private val taskIconDataSource: TaskIconDataSource,
private val taskVisualsChangedDelegate: TaskVisualsChangedDelegate,
- recentsCoroutineScope: CoroutineScope,
+ private val recentsCoroutineScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
) : RecentTasksRepository {
- private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
- private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
-
- private val taskData =
- groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } }
- private val visibleTasks =
- combine(taskData, visibleTaskIds) { tasks, visibleIds ->
- tasks.filter { it.key.id in visibleIds }
- }
-
- private val iconQueryResults: Flow<Map<Int, TaskIconQueryResponse?>> =
- visibleTasks
- .map { visibleTasksList -> visibleTasksList.map(::getIconDataRequest) }
- .flatMapLatest { iconRequestFlows: List<IconDataRequest> ->
- if (iconRequestFlows.isEmpty()) {
- flowOf(emptyMap())
- } else {
- combine(iconRequestFlows) { it.toMap() }
- }
- }
- .distinctUntilChanged()
-
- private val thumbnailQueryResults: Flow<Map<Int, ThumbnailData?>> =
- visibleTasks
- .map { visibleTasksList -> visibleTasksList.map(::getThumbnailDataRequest) }
- .flatMapLatest { thumbnailRequestFlows: List<ThumbnailDataRequest> ->
- if (thumbnailRequestFlows.isEmpty()) {
- flowOf(emptyMap())
- } else {
- combine(thumbnailRequestFlows) { it.toMap() }
- }
- }
- .distinctUntilChanged()
-
- private val augmentedTaskData: Flow<List<Task>> =
- combine(taskData, thumbnailQueryResults, iconQueryResults) {
- tasks,
- thumbnailQueryResults,
- iconQueryResults ->
- tasks.onEach { task ->
- // Add retrieved thumbnails + remove unnecessary thumbnails (e.g. invisible)
- task.thumbnail = thumbnailQueryResults[task.key.id]
-
- // TODO(b/352331675) don't load icons for DesktopTaskView
- // Add retrieved icons + remove unnecessary icons
- val iconQueryResult = iconQueryResults[task.key.id]
- task.icon = iconQueryResult?.icon
- task.titleDescription = iconQueryResult?.contentDescription
- task.title = iconQueryResult?.title
- }
- }
- .flowOn(dispatcherProvider.io)
- .shareIn(recentsCoroutineScope, SharingStarted.WhileSubscribed(5000), replay = 1)
+ private val tasks = MutableStateFlow(MapForStateFlow<Int, Task>(emptyMap()))
+ private val taskRequests = HashMap<Int, Pair<Task.TaskKey, Job>>()
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
if (forceRefresh) {
- recentsModel.getTasks { groupedTaskData.value = it }
+ 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)
+ }
+ )
+ }
}
- return augmentedTaskData
+ return tasks.map { it.values.toList() }
}
- override fun getTaskDataById(taskId: Int): Flow<Task?> =
- augmentedTaskData.map { taskList -> taskList.firstOrNull { it.key.id == taskId } }
+ override fun getTaskDataById(taskId: Int) = tasks.map { it[taskId] }
- override fun getThumbnailById(taskId: Int): Flow<ThumbnailData?> =
+ override fun getThumbnailById(taskId: Int) =
getTaskDataById(taskId).map { it?.thumbnail }.distinctUntilChangedBy { it?.snapshotId }
- override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
+ override fun setVisibleTasks(visibleTaskIdList: Set<Int>) {
Log.d(TAG, "setVisibleTasks: $visibleTaskIdList")
- this.visibleTaskIds.value = visibleTaskIdList.toSet()
+
+ // Remove tasks are no longer visible
+ val tasksNoLongerVisible = taskRequests.keys.subtract(visibleTaskIdList)
+ removeTasks(tasksNoLongerVisible)
+ // Add new tasks to be requested
+ visibleTaskIdList.subtract(taskRequests.keys).forEach { taskId -> requestTaskData(taskId) }
}
- /** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
- private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest = callbackFlow {
- trySend(task.key.id to task.thumbnail)
- trySend(task.key.id to getThumbnailFromDataSource(task))
+ private fun requestTaskData(taskId: Int) {
+ Log.i(TAG, "requestTaskData: $taskId")
+ val task = tasks.value[taskId] ?: return
+ taskRequests[taskId] =
+ Pair(
+ task.key,
+ recentsCoroutineScope.launch {
+ fetchIcon(task)
+ fetchThumbnail(task)
+ },
+ )
+ }
- val callback =
+ private fun removeTasks(tasksToRemove: Set<Int>) {
+ if (tasksToRemove.isEmpty()) return
+
+ tasksToRemove.forEach { taskId ->
+ Log.i(TAG, "removeTask: $taskId")
+ val request = taskRequests.remove(taskId) ?: return
+ val (taskKey, job) = request
+ job.cancel()
+
+ // un-registering callbacks
+ taskVisualsChangedDelegate.unregisterTaskIconChangedCallback(taskKey)
+ taskVisualsChangedDelegate.unregisterTaskThumbnailChangedCallback(taskKey)
+
+ // Clearing Task to reduce memory footprint
+ tasks.value[taskId]?.apply {
+ thumbnail = null
+ icon = null
+ title = null
+ titleDescription = null
+ }
+ }
+ tasks.update { oldValue -> MapForStateFlow(oldValue) }
+ }
+
+ private suspend fun fetchIcon(task: Task) {
+ updateIcon(task.key.id, getIconFromDataSource(task)) // Fetch icon from cache
+ taskVisualsChangedDelegate.registerTaskIconChangedCallback(
+ task.key,
+ object : TaskIconChangedCallback {
+ override fun onTaskIconChanged() {
+ recentsCoroutineScope.launch {
+ updateIcon(task.key.id, getIconFromDataSource(task))
+ }
+ }
+ },
+ )
+ }
+
+ private suspend fun fetchThumbnail(task: Task) {
+ updateThumbnail(task.key.id, getThumbnailFromDataSource(task))
+ taskVisualsChangedDelegate.registerTaskThumbnailChangedCallback(
+ task.key,
object : TaskThumbnailChangedCallback {
override fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?) {
- trySend(task.key.id to thumbnailData)
+ updateThumbnail(task.key.id, thumbnailData)
}
override fun onHighResLoadingStateChanged() {
- launch { trySend(task.key.id to getThumbnailFromDataSource(task)) }
+ recentsCoroutineScope.launch {
+ updateThumbnail(task.key.id, getThumbnailFromDataSource(task))
+ }
}
- }
- taskVisualsChangedDelegate.registerTaskThumbnailChangedCallback(task.key, callback)
- awaitClose { taskVisualsChangedDelegate.unregisterTaskThumbnailChangedCallback(task.key) }
+ },
+ )
}
- /** Flow wrapper for [TaskIconDataSource.getIconInBackground] api */
- private fun getIconDataRequest(task: Task): IconDataRequest =
- callbackFlow {
- trySend(task.key.id to task.getTaskIconQueryResponse())
- trySend(task.key.id to getIconFromDataSource(task))
+ private fun updateIcon(taskId: Int, iconData: IconData) {
+ val task = tasks.value[taskId] ?: return
+ task.icon = iconData.icon
+ task.titleDescription = iconData.contentDescription
+ task.title = iconData.title
+ tasks.update { oldValue -> MapForStateFlow(oldValue + (taskId to task)) }
+ }
- val callback =
- object : TaskIconChangedCallback {
- override fun onTaskIconChanged() {
- launch { trySend(task.key.id to getIconFromDataSource(task)) }
- }
- }
- taskVisualsChangedDelegate.registerTaskIconChangedCallback(task.key, callback)
- awaitClose {
- taskVisualsChangedDelegate.unregisterTaskIconChangedCallback(task.key)
- }
- }
- .distinctUntilChanged()
+ private fun updateThumbnail(taskId: Int, thumbnail: ThumbnailData?) {
+ val task = tasks.value[taskId] ?: return
+ task.thumbnail = thumbnail
+ tasks.update { oldValue -> MapForStateFlow(oldValue + (taskId to task)) }
+ }
private suspend fun getThumbnailFromDataSource(task: Task) =
withContext(dispatcherProvider.main) {
@@ -184,11 +184,7 @@
->
icon.constantState?.let {
continuation.resume(
- TaskIconQueryResponse(
- it.newDrawable().mutate(),
- contentDescription,
- title,
- )
+ IconData(it.newDrawable().mutate(), contentDescription, title)
)
}
}
@@ -199,22 +195,16 @@
companion object {
private const val TAG = "TasksRepository"
}
+
+ /** Helper class to support StateFlow emissions when using a Map with a MutableStateFlow. */
+ private data class MapForStateFlow<K, T>(
+ private val backingMap: Map<K, T>,
+ private val updated: Long = System.nanoTime(),
+ ) : Map<K, T> by backingMap
+
+ private data class IconData(
+ val icon: Drawable,
+ val contentDescription: String,
+ val title: String,
+ )
}
-
-data class TaskIconQueryResponse(
- val icon: Drawable,
- val contentDescription: String,
- val title: String,
-)
-
-private fun Task.getTaskIconQueryResponse(): TaskIconQueryResponse? {
- val iconVal = icon ?: return null
- val titleDescriptionVal = titleDescription ?: return null
- val titleVal = title ?: return null
-
- return TaskIconQueryResponse(iconVal, titleDescriptionVal, titleVal)
-}
-
-private typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>>
-
-private typealias IconDataRequest = Flow<Pair<Int, TaskIconQueryResponse?>>
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index 5cf6823..c511005 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -31,7 +31,7 @@
}
fun updateVisibleTasks(visibleTaskIdList: List<Int>) {
- recentsTasksRepository.setVisibleTasks(visibleTaskIdList)
+ recentsTasksRepository.setVisibleTasks(visibleTaskIdList.toSet())
}
fun updateScale(scale: Float) {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
index 7a17872..d6688d6 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
@@ -16,6 +16,7 @@
package com.android.quickstep.recents.data
+import android.graphics.drawable.Drawable
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
import kotlinx.coroutines.flow.Flow
@@ -25,9 +26,9 @@
class FakeTasksRepository : RecentTasksRepository {
private var thumbnailDataMap: Map<Int, ThumbnailData> = emptyMap()
- private var taskIconDataMap: Map<Int, TaskIconQueryResponse> = emptyMap()
+ private var taskIconDataMap: Map<Int, FakeIconData> = emptyMap()
private var tasks: MutableStateFlow<List<Task>> = MutableStateFlow(emptyList())
- private var visibleTasks: MutableStateFlow<List<Int>> = MutableStateFlow(emptyList())
+ private var visibleTasks: MutableStateFlow<Set<Int>> = MutableStateFlow(emptySet())
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> = tasks
@@ -48,16 +49,16 @@
override fun getThumbnailById(taskId: Int): Flow<ThumbnailData?> =
getTaskDataById(taskId).map { it?.thumbnail }
- override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
+ override fun setVisibleTasks(visibleTaskIdList: Set<Int>) {
visibleTasks.value = visibleTaskIdList
tasks.value =
tasks.value.map {
it.apply {
thumbnail = thumbnailDataMap[it.key.id]
- taskIconDataMap[it.key.id].let { taskIconData ->
- icon = taskIconData?.icon
- titleDescription = taskIconData?.contentDescription
- title = taskIconData?.title
+ taskIconDataMap[it.key.id].let { data ->
+ title = data?.title
+ titleDescription = data?.titleDescription
+ icon = data?.icon
}
}
}
@@ -71,7 +72,14 @@
this.thumbnailDataMap = thumbnailDataMap
}
- fun seedIconData(iconDataMap: Map<Int, TaskIconQueryResponse>) {
- this.taskIconDataMap = iconDataMap
+ fun seedIconData(id: Int, title: String, contentDescription: String, icon: Drawable) {
+ val iconData = FakeIconData(icon, contentDescription, title)
+ this.taskIconDataMap = mapOf(id to iconData)
}
+
+ private data class FakeIconData(
+ val icon: Drawable,
+ val titleDescription: String,
+ val title: String,
+ )
}
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 d55f2e3..357df6e 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
@@ -74,7 +74,6 @@
fun getAllTaskDataReturnsFlattenedListOfTasks() =
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)
-
assertThat(systemUnderTest.getAllTaskData(forceRefresh = true).first()).isEqualTo(tasks)
}
@@ -95,7 +94,7 @@
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1, 2))
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
assertThat(systemUnderTest.getTaskDataById(1).first()!!.thumbnail!!.thumbnail)
.isEqualTo(bitmap1)
@@ -109,7 +108,7 @@
recentsModel.seedTasks(defaultTaskList)
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1, 2))
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
systemUnderTest
.getTaskDataById(1)
@@ -128,14 +127,14 @@
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1, 2))
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail!!.thumbnail)
.isEqualTo(bitmap2)
// Prevent new loading of Bitmaps
taskThumbnailDataSource.shouldLoadSynchronously = false
- systemUnderTest.setVisibleTasks(listOf(2, 3))
+ systemUnderTest.setVisibleTasks(setOf(2, 3))
assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail!!.thumbnail)
.isEqualTo(bitmap2)
@@ -147,7 +146,7 @@
recentsModel.seedTasks(defaultTaskList)
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1, 2))
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
systemUnderTest
.getTaskDataById(2)
@@ -156,7 +155,7 @@
// Prevent new loading of Drawables
taskThumbnailDataSource.shouldLoadSynchronously = false
- systemUnderTest.setVisibleTasks(listOf(2, 3))
+ systemUnderTest.setVisibleTasks(setOf(2, 3))
systemUnderTest
.getTaskDataById(2)
@@ -171,7 +170,7 @@
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1, 2))
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
val task2 = systemUnderTest.getTaskDataById(2).first()!!
assertThat(task2.thumbnail!!.thumbnail).isEqualTo(bitmap2)
@@ -180,7 +179,7 @@
// Prevent new loading of Bitmaps
taskThumbnailDataSource.shouldLoadSynchronously = false
taskIconDataSource.shouldLoadSynchronously = false
- systemUnderTest.setVisibleTasks(listOf(0, 1))
+ systemUnderTest.setVisibleTasks(setOf(0, 1))
val task2AfterVisibleTasksChanged = systemUnderTest.getTaskDataById(2).first()!!
assertThat(task2AfterVisibleTasksChanged.thumbnail).isNull()
@@ -199,7 +198,7 @@
// Setup TasksRepository
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1, 2))
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
// Assert there is no bitmap in first emission
assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail).isNull()
@@ -217,8 +216,7 @@
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)
systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1))
+ systemUnderTest.setVisibleTasks(setOf(1))
val expectedThumbnailData = createThumbnailData()
val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
@@ -230,7 +228,7 @@
}
taskVisualsChangedDelegate.onTaskThumbnailChanged(1, expectedThumbnailData)
- assertThat(task1ThumbnailValues[1]!!.thumbnail).isEqualTo(expectedPreviousBitmap)
+ assertThat(task1ThumbnailValues.first()!!.thumbnail).isEqualTo(expectedPreviousBitmap)
assertThat(task1ThumbnailValues.last()).isEqualTo(expectedThumbnailData)
}
@@ -240,7 +238,7 @@
recentsModel.seedTasks(defaultTaskList)
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1))
+ systemUnderTest.setVisibleTasks(setOf(1))
val expectedBitmap = mock<Bitmap>()
val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
@@ -250,10 +248,11 @@
testScope.backgroundScope.launch {
taskDataFlow.map { it?.thumbnail?.thumbnail }.toList(task1ThumbnailValues)
}
+
taskThumbnailDataSource.taskIdToBitmap[1] = expectedBitmap
taskVisualsChangedDelegate.onHighResLoadingStateChanged(true)
- assertThat(task1ThumbnailValues[1]).isEqualTo(expectedPreviousBitmap)
+ assertThat(task1ThumbnailValues.first()).isEqualTo(expectedPreviousBitmap)
assertThat(task1ThumbnailValues.last()).isEqualTo(expectedBitmap)
}
@@ -263,7 +262,7 @@
recentsModel.seedTasks(defaultTaskList)
systemUnderTest.getAllTaskData(forceRefresh = true)
- systemUnderTest.setVisibleTasks(listOf(1))
+ systemUnderTest.setVisibleTasks(setOf(1))
val expectedIcon = FakeTaskIconDataSource.mockCopyableDrawable()
val expectedPreviousIcon = taskIconDataSource.taskIdToDrawable[1]
@@ -276,10 +275,34 @@
taskIconDataSource.taskIdToDrawable[1] = expectedIcon
taskVisualsChangedDelegate.onTaskIconChanged(1)
- assertThat(task1IconValues[1]).isEqualTo(expectedPreviousIcon)
+ assertThat(task1IconValues.first()).isEqualTo(expectedPreviousIcon)
assertThat(task1IconValues.last()).isEqualTo(expectedIcon)
}
+ @Test
+ fun setVisibleTasks_multipleTimesWithDifferentTasks_reusesThumbnailRequests() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+ taskThumbnailDataSource.shouldLoadSynchronously = false
+
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+ val task1IconValues = mutableListOf<Drawable?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.icon }.toList(task1IconValues)
+ }
+
+ systemUnderTest.setVisibleTasks(setOf(1))
+ val task1UpdatingTaskOld = taskThumbnailDataSource.taskIdToUpdatingTask[1]
+ println(task1UpdatingTaskOld)
+
+ systemUnderTest.setVisibleTasks(setOf(1, 2))
+ val task1UpdatingTaskNew = taskThumbnailDataSource.taskIdToUpdatingTask[1]
+ println(task1UpdatingTaskNew)
+
+ assertThat(task1UpdatingTaskNew).isEqualTo(task1UpdatingTaskOld)
+ }
+
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000))
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt
index 02f1d11..bd7d970 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt
@@ -66,7 +66,7 @@
deviceProfileRepository,
rotationStateRepository,
tasksRepository,
- previewPositionHelper
+ previewPositionHelper,
)
@Test
@@ -80,7 +80,7 @@
@Test
fun visibleTaskWithoutThumbnailData_returnsIdentityMatrix() = runTest {
tasksRepository.seedTasks(listOf(task))
- tasksRepository.setVisibleTasks(listOf(TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
assertThat(systemUnderTest.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl = true))
.isInstanceOf(MissingThumbnail::class.java)
@@ -90,7 +90,7 @@
fun visibleTaskWithThumbnailData_returnsTransformedMatrix() = runTest {
tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
tasksRepository.seedTasks(listOf(task))
- tasksRepository.setVisibleTasks(listOf(TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
val isLargeScreen = true
deviceProfileRepository.setRecentsDeviceProfile(
@@ -119,7 +119,7 @@
CANVAS_HEIGHT,
isLargeScreen,
activityRotation,
- isRtl
+ isRtl,
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt
index 12a94cf..73aa460 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailUseCaseTest.kt
@@ -71,7 +71,7 @@
fun taskVisible_returnsThumbnail() {
tasksRepository.seedTasks(listOf(task))
tasksRepository.seedThumbnailData(mapOf(TaskOverlayViewModelTest.TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(listOf(TaskOverlayViewModelTest.TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(TaskOverlayViewModelTest.TASK_ID))
assertThat(systemUnderTest.run(TASK_ID)).isEqualTo(thumbnailData.thumbnail)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/SysUiStatusNavFlagsUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/SysUiStatusNavFlagsUseCaseTest.kt
index ba4e206..92f2efd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/SysUiStatusNavFlagsUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/SysUiStatusNavFlagsUseCaseTest.kt
@@ -71,7 +71,7 @@
whenever(width).thenReturn(THUMBNAIL_WIDTH)
whenever(height).thenReturn(THUMBNAIL_HEIGHT)
},
- appearance = APPEARANCE_LIGHT_THEME
+ appearance = APPEARANCE_LIGHT_THEME,
)
val secondTask =
@@ -85,14 +85,14 @@
whenever(width).thenReturn(THUMBNAIL_WIDTH)
whenever(height).thenReturn(THUMBNAIL_HEIGHT)
},
- appearance = APPEARANCE_DARK_THEME
+ appearance = APPEARANCE_DARK_THEME,
)
tasksRepository.seedTasks(listOf(firstTask, secondTask))
tasksRepository.seedThumbnailData(
mapOf(FIRST_TASK_ID to firstThumbnailData, SECOND_TASK_ID to secondThumbnailData)
)
- tasksRepository.setVisibleTasks(listOf(FIRST_TASK_ID, SECOND_TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(FIRST_TASK_ID, SECOND_TASK_ID))
}
companion object {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
index 829987c..a32e07d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
@@ -97,7 +97,7 @@
)
// setVisibleTasks forces FakeTasksRepository to update the flows returned by
// getThumbnailById
- tasksRepository.setVisibleTasks(listOf(1, 2))
+ tasksRepository.setVisibleTasks(setOf(1, 2))
// Then wait for thumbnailData should complete, and the previous getThumbnailById flow
// should return updated values
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCaseTest.kt
index a584d71..0767fb9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCaseTest.kt
@@ -25,7 +25,6 @@
import android.view.Surface.ROTATION_90
import com.android.quickstep.recents.data.FakeRecentsRotationStateRepository
import com.android.quickstep.recents.data.FakeTasksRepository
-import com.android.quickstep.recents.data.TaskIconQueryResponse
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.systemui.shared.recents.model.Task
@@ -49,7 +48,7 @@
taskContainerData,
taskThumbnailViewData,
recentTasksRepository,
- recentsRotationStateRepository
+ recentsRotationStateRepository,
)
@Test
@@ -117,16 +116,16 @@
private fun setupTask(taskId: Int, thumbnailData: ThumbnailData = createThumbnailData()) {
recentTasksRepository.seedThumbnailData(mapOf(taskId to thumbnailData))
- val expectedIconData = createIconData("Task $taskId")
- recentTasksRepository.seedIconData(mapOf(taskId to expectedIconData))
+ val expectedIconData = mock<Drawable>()
+ recentTasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
recentTasksRepository.seedTasks(tasks)
- recentTasksRepository.setVisibleTasks(listOf(taskId))
+ recentTasksRepository.setVisibleTasks(setOf(taskId))
}
private fun createThumbnailData(
rotation: Int = Surface.ROTATION_0,
width: Int = THUMBNAIL_WIDTH,
- height: Int = THUMBNAIL_HEIGHT
+ height: Int = THUMBNAIL_HEIGHT,
): ThumbnailData {
val bitmap = mock<Bitmap>()
whenever(bitmap.width).thenReturn(width)
@@ -135,8 +134,6 @@
return ThumbnailData(thumbnail = bitmap, rotation = rotation)
}
- private fun createIconData(title: String) = TaskIconQueryResponse(mock<Drawable>(), "", title)
-
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
colorBackground = Color.argb(taskId, taskId, taskId, taskId)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
index c88a3fc..c541d3d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelImplTest.kt
@@ -25,7 +25,6 @@
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.quickstep.recents.data.FakeTasksRepository
-import com.android.quickstep.recents.data.TaskIconQueryResponse
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MissingThumbnail
@@ -81,7 +80,7 @@
fun bindRunningTask_thenStateIs_LiveTile() = runTest {
val taskId = 1
tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(taskId))
+ tasksRepository.setVisibleTasks(setOf(taskId))
recentsViewData.runningTaskIds.value = setOf(taskId)
systemUnderTest.bind(taskId)
@@ -93,10 +92,10 @@
val taskId = 1
val expectedThumbnailData = createThumbnailData()
tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = createIconData("Task 1")
- tasksRepository.seedIconData(mapOf(taskId to expectedIconData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(taskId))
+ tasksRepository.setVisibleTasks(setOf(taskId))
recentsViewData.runningTaskIds.value = setOf(taskId)
recentsViewData.runningTaskShowScreenshot.value = true
systemUnderTest.bind(taskId)
@@ -109,7 +108,7 @@
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_0,
),
- expectedIconData.icon,
+ expectedIconData,
)
)
}
@@ -151,7 +150,7 @@
val runningTaskId = 1
val stoppedTaskId = 2
tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(runningTaskId, stoppedTaskId))
+ tasksRepository.setVisibleTasks(setOf(runningTaskId, stoppedTaskId))
recentsViewData.runningTaskIds.value = setOf(runningTaskId)
systemUnderTest.bind(runningTaskId)
assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
@@ -165,7 +164,7 @@
fun bindStoppedTaskWithoutThumbnail_thenStateIs_BackgroundOnly_withAlphaRemoved() = runTest {
val stoppedTaskId = 2
tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(stoppedTaskId))
+ tasksRepository.setVisibleTasks(setOf(stoppedTaskId))
systemUnderTest.bind(stoppedTaskId)
assertThat(systemUnderTest.uiState.first())
@@ -178,7 +177,7 @@
tasksRepository.seedThumbnailData(mapOf(taskId to createThumbnailData()))
tasks[taskId].isLocked = true
tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(taskId))
+ tasksRepository.setVisibleTasks(setOf(taskId))
systemUnderTest.bind(taskId)
assertThat(systemUnderTest.uiState.first())
@@ -190,10 +189,10 @@
val taskId = 2
val expectedThumbnailData = createThumbnailData(rotation = Surface.ROTATION_270)
tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = createIconData("Task 2")
- tasksRepository.seedIconData(mapOf(taskId to expectedIconData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(taskId))
+ tasksRepository.setVisibleTasks(setOf(taskId))
systemUnderTest.bind(taskId)
assertThat(systemUnderTest.uiState.first())
@@ -204,7 +203,7 @@
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_270,
),
- expectedIconData.icon,
+ expectedIconData,
)
)
}
@@ -214,14 +213,14 @@
val taskId = 2
val expectedThumbnailData = createThumbnailData()
tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = createIconData("Task 2")
- tasksRepository.seedIconData(mapOf(taskId to expectedIconData))
+ val expectedIconData = mock<Drawable>()
+ tasksRepository.seedIconData(taskId, "Task $taskId", "", expectedIconData)
tasksRepository.seedTasks(tasks)
systemUnderTest.bind(taskId)
assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
- tasksRepository.setVisibleTasks(listOf(taskId))
+ tasksRepository.setVisibleTasks(setOf(taskId))
assertThat(systemUnderTest.uiState.first())
.isEqualTo(
SnapshotSplash(
@@ -230,7 +229,7 @@
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_0,
),
- expectedIconData.icon,
+ expectedIconData,
)
)
}
@@ -295,8 +294,6 @@
return ThumbnailData(thumbnail = bitmap, rotation = rotation)
}
- private fun createIconData(title: String) = TaskIconQueryResponse(mock<Drawable>(), "", title)
-
companion object {
const val THUMBNAIL_WIDTH = 100
const val THUMBNAIL_HEIGHT = 200
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt
index d0887df..2e91f5c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/viewmodel/TaskOverlayViewModelTest.kt
@@ -89,12 +89,7 @@
task.isLocked = false
assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(
- Enabled(
- isRealSnapshot = false,
- thumbnail = null,
- )
- )
+ .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = null))
}
@Test
@@ -103,17 +98,12 @@
recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
tasksRepository.seedTasks(listOf(task))
tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(listOf(TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
thumbnailData.isRealSnapshot = true
task.isLocked = false
assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(
- Enabled(
- isRealSnapshot = true,
- thumbnail = thumbnailData.thumbnail,
- )
- )
+ .isEqualTo(Enabled(isRealSnapshot = true, thumbnail = thumbnailData.thumbnail))
}
@Test
@@ -122,17 +112,12 @@
recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
tasksRepository.seedTasks(listOf(task))
tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(listOf(TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
thumbnailData.isRealSnapshot = true
task.isLocked = true
assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(
- Enabled(
- isRealSnapshot = false,
- thumbnail = thumbnailData.thumbnail,
- )
- )
+ .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
}
@Test
@@ -141,17 +126,12 @@
recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
tasksRepository.seedTasks(listOf(task))
tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
- tasksRepository.setVisibleTasks(listOf(TASK_ID))
+ tasksRepository.setVisibleTasks(setOf(TASK_ID))
thumbnailData.isRealSnapshot = false
task.isLocked = false
assertThat(systemUnderTest.overlayState.first())
- .isEqualTo(
- Enabled(
- isRealSnapshot = false,
- thumbnail = thumbnailData.thumbnail,
- )
- )
+ .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
}
@Test