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
18 
19 import android.content.ComponentName
20 import android.os.UserHandle
21 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
22 import com.android.systemui.mediaprojection.appselector.data.RecentTask
23 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
24 import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
25 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
26 import com.android.systemui.shared.recents.model.ThumbnailData
27 import javax.inject.Inject
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.Deferred
30 import kotlinx.coroutines.async
31 import kotlinx.coroutines.cancel
32 import kotlinx.coroutines.coroutineScope
33 import kotlinx.coroutines.launch
34 
35 @MediaProjectionAppSelectorScope
36 class MediaProjectionAppSelectorController
37 @Inject
38 constructor(
39     private val recentTaskListProvider: RecentTaskListProvider,
40     private val view: MediaProjectionAppSelectorView,
41     private val devicePolicyResolver: ScreenCaptureDevicePolicyResolver,
42     @HostUserHandle private val hostUserHandle: UserHandle,
43     @MediaProjectionAppSelector private val scope: CoroutineScope,
44     @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName,
45     @MediaProjectionAppSelector private val callerPackageName: String?,
46     private val thumbnailLoader: RecentTaskThumbnailLoader,
47     @MediaProjectionAppSelector private val isFirstStart: Boolean,
48     private val logger: MediaProjectionMetricsLogger,
49     @HostUid private val hostUid: Int,
50 ) {
51 
52     fun init() {
53         // Only log during the first start of the app selector.
54         // Don't log when the app selector restarts due to a config change.
55         if (isFirstStart) {
56             logger.notifyAppSelectorDisplayed(hostUid)
57         }
58 
59         scope.launch {
60             val recentTasks = recentTaskListProvider.loadRecentTasks()
61 
62             val tasks =
63                 recentTasks.filterDevicePolicyRestrictedTasks().filterAppSelector().sortedTasks()
64 
65             // Thumbnails are not fresh for the foreground task(s). They are only refreshed at
66             // launch, going to home, or going to overview.
67             // For this reason, we need to refresh them here.
68             refreshForegroundTaskThumbnails(tasks)
69 
70             view.bind(tasks)
71         }
72     }
73 
74     fun destroy() {
75         scope.cancel()
76     }
77 
78     fun onSelectorDismissed() {
79         logger.notifyProjectionRequestCancelled(hostUid)
80     }
81 
82     private suspend fun refreshForegroundTaskThumbnails(tasks: List<RecentTask>) {
83         coroutineScope {
84             val thumbnails: List<Deferred<ThumbnailData?>> =
85                 tasks
86                     .filter { it.isForegroundTask }
87                     .map { async { thumbnailLoader.captureThumbnail(it.taskId) } }
88             thumbnails.forEach { thumbnail -> thumbnail.await() }
89         }
90     }
91 
92     /** Removes all recent tasks that should be blocked according to the policy */
93     private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = filter {
94         devicePolicyResolver.isScreenCaptureAllowed(
95             targetAppUserHandle = UserHandle.of(it.userId),
96             hostAppUserHandle = hostUserHandle
97         )
98     }
99 
100     private fun List<RecentTask>.filterAppSelector(): List<RecentTask> = filter {
101         // Only take tasks that is not the app selector
102         it.topActivityComponent != appSelectorComponentName
103     }
104 
105     private fun List<RecentTask>.sortedTasks(): List<RecentTask> = sortedBy {
106         // Show normal tasks first and only then tasks with opened app selector
107         it.topActivityComponent?.packageName == callerPackageName
108     }
109 }
110