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