1 /* <lambda>null2 * 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.domain.interactor 18 19 import android.app.ActivityManager.RunningTaskInfo 20 import android.app.TaskInfo 21 import android.content.Intent 22 import android.util.Log 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.mediaprojection.data.model.MediaProjectionState 25 import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository 26 import com.android.systemui.mediaprojection.taskswitcher.data.repository.TasksRepository 27 import com.android.systemui.mediaprojection.taskswitcher.domain.model.TaskSwitchState 28 import javax.inject.Inject 29 import kotlinx.coroutines.ExperimentalCoroutinesApi 30 import kotlinx.coroutines.flow.Flow 31 import kotlinx.coroutines.flow.flatMapLatest 32 import kotlinx.coroutines.flow.flowOf 33 import kotlinx.coroutines.flow.map 34 35 /** Interactor with logic related to task switching in the context of media projection. */ 36 @OptIn(ExperimentalCoroutinesApi::class) 37 @SysUISingleton 38 class TaskSwitchInteractor 39 @Inject 40 constructor( 41 private val mediaProjectionRepository: MediaProjectionRepository, 42 private val tasksRepository: TasksRepository, 43 ) { 44 45 suspend fun switchProjectedTask(task: RunningTaskInfo) { 46 mediaProjectionRepository.switchProjectedTask(task) 47 } 48 49 suspend fun goBackToTask(task: RunningTaskInfo) { 50 tasksRepository.launchRecentTask(task) 51 } 52 53 /** 54 * Emits a stream of changes to the state of task switching, in the context of media projection. 55 */ 56 val taskSwitchChanges: Flow<TaskSwitchState> = 57 mediaProjectionRepository.mediaProjectionState.flatMapLatest { projectionState -> 58 Log.d(TAG, "MediaProjectionState -> $projectionState") 59 when (projectionState) { 60 is MediaProjectionState.Projecting.SingleTask -> { 61 val projectedTask = projectionState.task 62 tasksRepository.foregroundTask.map { foregroundTask -> 63 if (hasForegroundTaskSwitched(projectedTask, foregroundTask)) { 64 TaskSwitchState.TaskSwitched(projectedTask, foregroundTask) 65 } else { 66 TaskSwitchState.TaskUnchanged 67 } 68 } 69 } 70 is MediaProjectionState.Projecting.EntireScreen, 71 is MediaProjectionState.NotProjecting -> { 72 flowOf(TaskSwitchState.NotProjectingTask) 73 } 74 } 75 } 76 77 /** 78 * Returns whether tasks have been switched. 79 * 80 * Always returns `false` when launcher is in the foreground. The reason is that when going to 81 * recents to switch apps, launcher becomes the new foreground task, and we don't want to show 82 * the notification then. 83 */ 84 private fun hasForegroundTaskSwitched(projectedTask: TaskInfo, foregroundTask: TaskInfo) = 85 projectedTask.taskId != foregroundTask.taskId && !foregroundTask.isLauncher 86 87 private val TaskInfo.isLauncher 88 get() = 89 baseIntent.hasCategory(Intent.CATEGORY_HOME) && baseIntent.action == Intent.ACTION_MAIN 90 91 companion object { 92 private const val TAG = "TaskSwitchInteractor" 93 } 94 } 95