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