1 /*
2  * Copyright (C) 2023 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.taskswitcher.data.repository
18 
19 import android.app.ActivityManager.RunningTaskInfo
20 import android.app.ActivityTaskManager
21 import android.app.TaskStackListener
22 import android.os.IBinder
23 import android.util.Log
24 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
25 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dagger.qualifiers.Application
28 import com.android.systemui.dagger.qualifiers.Background
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineDispatcher
31 import kotlinx.coroutines.CoroutineScope
32 import kotlinx.coroutines.channels.awaitClose
33 import kotlinx.coroutines.flow.Flow
34 import kotlinx.coroutines.flow.SharingStarted
35 import kotlinx.coroutines.flow.shareIn
36 import kotlinx.coroutines.withContext
37 
38 /** Implementation of [TasksRepository] that uses [ActivityTaskManager] as the data source. */
39 @SysUISingleton
40 class ActivityTaskManagerTasksRepository
41 @Inject
42 constructor(
43     private val activityTaskManager: ActivityTaskManager,
44     @Application private val applicationScope: CoroutineScope,
45     @Background private val backgroundDispatcher: CoroutineDispatcher,
46 ) : TasksRepository {
47 
48     override suspend fun findRunningTaskFromWindowContainerToken(
49         windowContainerToken: IBinder
50     ): RunningTaskInfo? =
51         getRunningTasks().firstOrNull { taskInfo ->
52             taskInfo.token.asBinder() == windowContainerToken
53         }
54 
55     private suspend fun getRunningTasks(): List<RunningTaskInfo> =
56         withContext(backgroundDispatcher) { activityTaskManager.getTasks(Integer.MAX_VALUE) }
57 
58     override val foregroundTask: Flow<RunningTaskInfo> =
59         conflatedCallbackFlow {
60                 val listener =
61                     object : TaskStackListener() {
62                         override fun onTaskMovedToFront(taskInfo: RunningTaskInfo) {
63                             Log.d(TAG, "onTaskMovedToFront: $taskInfo")
64                             trySendWithFailureLogging(taskInfo, TAG)
65                         }
66                     }
67                 activityTaskManager.registerTaskStackListener(listener)
68                 awaitClose { activityTaskManager.unregisterTaskStackListener(listener) }
69             }
70             .shareIn(applicationScope, SharingStarted.Lazily, replay = 1)
71 
72     companion object {
73         private const val TAG = "TasksRepository"
74     }
75 }
76