1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.quickstep.recents.data 18 19 import com.android.quickstep.TaskIconCache 20 import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource 21 import com.android.quickstep.util.GroupTask 22 import com.android.systemui.shared.recents.model.Task 23 import com.android.systemui.shared.recents.model.ThumbnailData 24 import kotlin.coroutines.resume 25 import kotlinx.coroutines.ExperimentalCoroutinesApi 26 import kotlinx.coroutines.flow.Flow 27 import kotlinx.coroutines.flow.MutableStateFlow 28 import kotlinx.coroutines.flow.combine 29 import kotlinx.coroutines.flow.distinctUntilChanged 30 import kotlinx.coroutines.flow.flatMapLatest 31 import kotlinx.coroutines.flow.flow 32 import kotlinx.coroutines.flow.flowOf 33 import kotlinx.coroutines.flow.map 34 import kotlinx.coroutines.suspendCancellableCoroutine 35 36 @OptIn(ExperimentalCoroutinesApi::class) 37 class TasksRepository( 38 private val recentsModel: RecentTasksDataSource, 39 private val taskThumbnailDataSource: TaskThumbnailDataSource, 40 private val taskIconCache: TaskIconCache, 41 ) : RecentTasksRepository { 42 private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>()) 43 private val _taskData = 44 groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } } 45 private val visibleTaskIds = MutableStateFlow(emptySet<Int>()) 46 47 private val taskData: Flow<List<Task>> = 48 combine(_taskData, getThumbnailQueryResults()) { tasks, results -> 49 tasks.forEach { task -> 50 // Add retrieved thumbnails + remove unnecessary thumbnails 51 task.thumbnail = results[task.key.id] 52 } 53 tasks 54 } 55 56 override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> { 57 if (forceRefresh) { 58 recentsModel.getTasks { groupedTaskData.value = it } 59 } 60 return taskData 61 } 62 63 override fun getTaskDataById(taskId: Int): Flow<Task?> = 64 taskData.map { taskList -> taskList.firstOrNull { it.key.id == taskId } } 65 66 override fun setVisibleTasks(visibleTaskIdList: List<Int>) { 67 this.visibleTaskIds.value = visibleTaskIdList.toSet() 68 } 69 70 /** Flow wrapper for [TaskThumbnailDataSource.updateThumbnailInBackground] api */ 71 private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest = 72 flow { 73 emit(task.key.id to task.thumbnail) 74 val thumbnailDataResult: ThumbnailData? = 75 suspendCancellableCoroutine { continuation -> 76 val cancellableTask = 77 taskThumbnailDataSource.updateThumbnailInBackground(task) { 78 continuation.resume(it) 79 } 80 continuation.invokeOnCancellation { cancellableTask?.cancel() } 81 } 82 emit(task.key.id to thumbnailDataResult) 83 } 84 .distinctUntilChanged() 85 86 /** 87 * This is a Flow that makes a query for thumbnail data to the [taskThumbnailDataSource] for 88 * each visible task. It then collects the responses and returns them in a Map as soon as they 89 * are available. 90 */ 91 private fun getThumbnailQueryResults(): Flow<Map<Int, ThumbnailData?>> { 92 val visibleTasks = 93 combine(_taskData, visibleTaskIds) { tasks, visibleIds -> 94 tasks.filter { it.key.id in visibleIds } 95 } 96 val visibleThumbnailDataRequests: Flow<List<ThumbnailDataRequest>> = 97 visibleTasks.map { 98 it.map { visibleTask -> 99 val taskCopy = Task(visibleTask).apply { thumbnail = visibleTask.thumbnail } 100 getThumbnailDataRequest(taskCopy) 101 } 102 } 103 return visibleThumbnailDataRequests.flatMapLatest { 104 thumbnailRequestFlows: List<ThumbnailDataRequest> -> 105 if (thumbnailRequestFlows.isEmpty()) { 106 flowOf(emptyMap()) 107 } else { 108 combine(thumbnailRequestFlows) { it.toMap() } 109 } 110 } 111 } 112 } 113 114 typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>> 115