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