1 /*
<lambda>null2  * Copyright (C) 2022 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.systemui.mediaprojection.appselector.data
18 
19 import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
20 import android.content.pm.UserInfo
21 import android.os.UserManager
22 import com.android.systemui.dagger.qualifiers.Background
23 import com.android.systemui.settings.UserTracker
24 import com.android.systemui.util.kotlin.getOrNull
25 import com.android.wm.shell.recents.RecentTasks
26 import com.android.wm.shell.util.GroupedRecentTaskInfo
27 import java.util.Optional
28 import java.util.concurrent.Executor
29 import javax.inject.Inject
30 import kotlin.coroutines.resume
31 import kotlin.coroutines.suspendCoroutine
32 import kotlinx.coroutines.CoroutineDispatcher
33 import kotlinx.coroutines.withContext
34 
35 interface RecentTaskListProvider {
36     /** Loads recent tasks, the returned task list is from the most-recent to least-recent order */
37     suspend fun loadRecentTasks(): List<RecentTask>
38 }
39 
40 class ShellRecentTaskListProvider
41 @Inject
42 constructor(
43     @Background private val coroutineDispatcher: CoroutineDispatcher,
44     @Background private val backgroundExecutor: Executor,
45     private val recentTasks: Optional<RecentTasks>,
46     private val userTracker: UserTracker,
47     private val userManager: UserManager,
48 ) : RecentTaskListProvider {
49 
<lambda>null50     private val recents by lazy { recentTasks.getOrNull() }
51 
loadRecentTasksnull52     override suspend fun loadRecentTasks(): List<RecentTask> =
53         withContext(coroutineDispatcher) {
54             val groupedTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
55             // Note: the returned task list is from the most-recent to least-recent order.
56             // When opening the app selector in full screen, index 0 will be just the app selector
57             // activity and a null second task, so the foreground task will be index 1, but when
58             // opening the app selector in split screen mode, the foreground task will be the second
59             // task in index 0.
60             val foregroundGroup =
61                 if (groupedTasks.firstOrNull()?.splitBounds != null) groupedTasks.first()
62                 else groupedTasks.elementAtOrNull(1)
63             val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId
64             val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId
65             val foregroundTaskIds = listOfNotNull(foregroundTaskId1, foregroundTaskId2)
66             groupedTasks.flatMap {
67                 val task1 =
68                     RecentTask(
69                         it.taskInfo1,
70                         it.taskInfo1.taskId in foregroundTaskIds && it.taskInfo1.isVisible,
71                         userManager.getUserInfo(it.taskInfo1.userId).toUserType(),
72                         it.splitBounds
73                     )
74 
75                 val task2 =
76                     if (it.taskInfo2 != null) {
77                         RecentTask(
78                             it.taskInfo2!!,
79                             it.taskInfo2!!.taskId in foregroundTaskIds && it.taskInfo2!!.isVisible,
80                             userManager.getUserInfo(it.taskInfo2!!.userId).toUserType(),
81                             it.splitBounds
82                         )
83                     } else null
84 
85                 listOfNotNull(task1, task2)
86             }
87         }
88 
getTasksnull89     private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
90         suspendCoroutine { continuation ->
91             getRecentTasks(
92                 Integer.MAX_VALUE,
93                 RECENT_IGNORE_UNAVAILABLE,
94                 userTracker.userId,
95                 backgroundExecutor
96             ) { tasks ->
97                 continuation.resume(tasks)
98             }
99         }
100 
UserInfonull101     private fun UserInfo.toUserType(): RecentTask.UserType =
102         if (isCloneProfile) {
103             RecentTask.UserType.CLONED
104         } else if (isManagedProfile) {
105             RecentTask.UserType.WORK
106         } else if (isPrivateProfile) {
107             RecentTask.UserType.PRIVATE
108         } else {
109             RecentTask.UserType.STANDARD
110         }
111 }
112