Loading packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt +15 −1 Original line number Diff line number Diff line Loading @@ -24,12 +24,14 @@ import android.media.projection.ReviewGrantedConsentResult import android.os.RemoteException import android.os.ServiceManager import android.util.Log import android.window.WindowContainerToken import javax.inject.Inject /** * Helper class that handles the media projection service related actions. It simplifies invoking * the MediaProjectionManagerService and updating the permission consent. */ class MediaProjectionServiceHelper { class MediaProjectionServiceHelper @Inject constructor() { companion object { private const val TAG = "MediaProjectionServiceHelper" private val service = Loading Loading @@ -90,4 +92,16 @@ class MediaProjectionServiceHelper { } } } /** Updates the projected task to the task that has a matching [WindowContainerToken]. */ fun updateTaskRecordingSession(token: WindowContainerToken): Boolean { return try { true // TODO: actually call the service once it is implemented // service.updateTaskRecordingSession(token) } catch (e: RemoteException) { Log.e(TAG, "Unable to updateTaskRecordingSession", e) false } } } packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt +2 −2 Original line number Diff line number Diff line Loading @@ -16,11 +16,11 @@ package com.android.systemui.mediaprojection.taskswitcher.data.model import android.app.TaskInfo import android.app.ActivityManager.RunningTaskInfo /** Represents the state of media projection. */ sealed interface MediaProjectionState { object NotProjecting : MediaProjectionState object EntireScreen : MediaProjectionState data class SingleTask(val task: TaskInfo) : MediaProjectionState data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState } packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt +26 −2 Original line number Diff line number Diff line Loading @@ -17,10 +17,14 @@ package com.android.systemui.mediaprojection.taskswitcher.data.repository import android.app.ActivityManager.RunningTaskInfo import android.app.ActivityOptions import android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import android.app.ActivityTaskManager import android.app.IActivityTaskManager import android.app.TaskStackListener import android.os.IBinder import android.util.Log import android.view.Display import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton Loading @@ -40,11 +44,24 @@ import kotlinx.coroutines.withContext class ActivityTaskManagerTasksRepository @Inject constructor( private val activityTaskManager: ActivityTaskManager, private val activityTaskManager: IActivityTaskManager, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : TasksRepository { override suspend fun launchRecentTask(taskInfo: RunningTaskInfo) { withContext(backgroundDispatcher) { val activityOptions = ActivityOptions.makeBasic() activityOptions.pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED activityOptions.launchDisplayId = taskInfo.displayId activityTaskManager.startActivityFromRecents( taskInfo.taskId, activityOptions.toBundle() ) } } override suspend fun findRunningTaskFromWindowContainerToken( windowContainerToken: IBinder ): RunningTaskInfo? = Loading @@ -53,7 +70,14 @@ constructor( } private suspend fun getRunningTasks(): List<RunningTaskInfo> = withContext(backgroundDispatcher) { activityTaskManager.getTasks(Integer.MAX_VALUE) } withContext(backgroundDispatcher) { activityTaskManager.getTasks( /* maxNum = */ Integer.MAX_VALUE, /* filterForVisibleRecents = */ false, /* keepIntentExtra = */ false, /* displayId = */ Display.INVALID_DISPLAY ) } override val foregroundTask: Flow<RunningTaskInfo> = conflatedCallbackFlow { Loading packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt +20 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.mediaprojection.taskswitcher.data.repository import android.app.ActivityManager.RunningTaskInfo import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager import android.os.Handler Loading @@ -26,15 +27,19 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.mediaprojection.MediaProjectionServiceHelper import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @SysUISingleton class MediaProjectionManagerRepository Loading @@ -43,9 +48,21 @@ constructor( private val mediaProjectionManager: MediaProjectionManager, @Main private val handler: Handler, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, private val tasksRepository: TasksRepository, private val mediaProjectionServiceHelper: MediaProjectionServiceHelper, ) : MediaProjectionRepository { override suspend fun switchProjectedTask(task: RunningTaskInfo) { withContext(backgroundDispatcher) { if (mediaProjectionServiceHelper.updateTaskRecordingSession(task.token)) { Log.d(TAG, "Successfully switched projected task") } else { Log.d(TAG, "Failed to switch projected task") } } } override val mediaProjectionState: Flow<MediaProjectionState> = conflatedCallbackFlow { val callback = Loading Loading @@ -82,7 +99,9 @@ constructor( } val matchingTask = tasksRepository.findRunningTaskFromWindowContainerToken( checkNotNull(session.tokenToRecord)) ?: return MediaProjectionState.EntireScreen checkNotNull(session.tokenToRecord) ) ?: return MediaProjectionState.EntireScreen return MediaProjectionState.SingleTask(matchingTask) } Loading packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,12 +16,16 @@ package com.android.systemui.mediaprojection.taskswitcher.data.repository import android.app.ActivityManager.RunningTaskInfo import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState import kotlinx.coroutines.flow.Flow /** Represents a repository to retrieve and change data related to media projection. */ interface MediaProjectionRepository { /** Switches the task that should be projected. */ suspend fun switchProjectedTask(task: RunningTaskInfo) /** Represents the current [MediaProjectionState]. */ val mediaProjectionState: Flow<MediaProjectionState> } Loading
packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt +15 −1 Original line number Diff line number Diff line Loading @@ -24,12 +24,14 @@ import android.media.projection.ReviewGrantedConsentResult import android.os.RemoteException import android.os.ServiceManager import android.util.Log import android.window.WindowContainerToken import javax.inject.Inject /** * Helper class that handles the media projection service related actions. It simplifies invoking * the MediaProjectionManagerService and updating the permission consent. */ class MediaProjectionServiceHelper { class MediaProjectionServiceHelper @Inject constructor() { companion object { private const val TAG = "MediaProjectionServiceHelper" private val service = Loading Loading @@ -90,4 +92,16 @@ class MediaProjectionServiceHelper { } } } /** Updates the projected task to the task that has a matching [WindowContainerToken]. */ fun updateTaskRecordingSession(token: WindowContainerToken): Boolean { return try { true // TODO: actually call the service once it is implemented // service.updateTaskRecordingSession(token) } catch (e: RemoteException) { Log.e(TAG, "Unable to updateTaskRecordingSession", e) false } } }
packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt +2 −2 Original line number Diff line number Diff line Loading @@ -16,11 +16,11 @@ package com.android.systemui.mediaprojection.taskswitcher.data.model import android.app.TaskInfo import android.app.ActivityManager.RunningTaskInfo /** Represents the state of media projection. */ sealed interface MediaProjectionState { object NotProjecting : MediaProjectionState object EntireScreen : MediaProjectionState data class SingleTask(val task: TaskInfo) : MediaProjectionState data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState }
packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt +26 −2 Original line number Diff line number Diff line Loading @@ -17,10 +17,14 @@ package com.android.systemui.mediaprojection.taskswitcher.data.repository import android.app.ActivityManager.RunningTaskInfo import android.app.ActivityOptions import android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import android.app.ActivityTaskManager import android.app.IActivityTaskManager import android.app.TaskStackListener import android.os.IBinder import android.util.Log import android.view.Display import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton Loading @@ -40,11 +44,24 @@ import kotlinx.coroutines.withContext class ActivityTaskManagerTasksRepository @Inject constructor( private val activityTaskManager: ActivityTaskManager, private val activityTaskManager: IActivityTaskManager, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : TasksRepository { override suspend fun launchRecentTask(taskInfo: RunningTaskInfo) { withContext(backgroundDispatcher) { val activityOptions = ActivityOptions.makeBasic() activityOptions.pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED activityOptions.launchDisplayId = taskInfo.displayId activityTaskManager.startActivityFromRecents( taskInfo.taskId, activityOptions.toBundle() ) } } override suspend fun findRunningTaskFromWindowContainerToken( windowContainerToken: IBinder ): RunningTaskInfo? = Loading @@ -53,7 +70,14 @@ constructor( } private suspend fun getRunningTasks(): List<RunningTaskInfo> = withContext(backgroundDispatcher) { activityTaskManager.getTasks(Integer.MAX_VALUE) } withContext(backgroundDispatcher) { activityTaskManager.getTasks( /* maxNum = */ Integer.MAX_VALUE, /* filterForVisibleRecents = */ false, /* keepIntentExtra = */ false, /* displayId = */ Display.INVALID_DISPLAY ) } override val foregroundTask: Flow<RunningTaskInfo> = conflatedCallbackFlow { Loading
packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt +20 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.mediaprojection.taskswitcher.data.repository import android.app.ActivityManager.RunningTaskInfo import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager import android.os.Handler Loading @@ -26,15 +27,19 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.mediaprojection.MediaProjectionServiceHelper import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @SysUISingleton class MediaProjectionManagerRepository Loading @@ -43,9 +48,21 @@ constructor( private val mediaProjectionManager: MediaProjectionManager, @Main private val handler: Handler, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, private val tasksRepository: TasksRepository, private val mediaProjectionServiceHelper: MediaProjectionServiceHelper, ) : MediaProjectionRepository { override suspend fun switchProjectedTask(task: RunningTaskInfo) { withContext(backgroundDispatcher) { if (mediaProjectionServiceHelper.updateTaskRecordingSession(task.token)) { Log.d(TAG, "Successfully switched projected task") } else { Log.d(TAG, "Failed to switch projected task") } } } override val mediaProjectionState: Flow<MediaProjectionState> = conflatedCallbackFlow { val callback = Loading Loading @@ -82,7 +99,9 @@ constructor( } val matchingTask = tasksRepository.findRunningTaskFromWindowContainerToken( checkNotNull(session.tokenToRecord)) ?: return MediaProjectionState.EntireScreen checkNotNull(session.tokenToRecord) ) ?: return MediaProjectionState.EntireScreen return MediaProjectionState.SingleTask(matchingTask) } Loading
packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,12 +16,16 @@ package com.android.systemui.mediaprojection.taskswitcher.data.repository import android.app.ActivityManager.RunningTaskInfo import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState import kotlinx.coroutines.flow.Flow /** Represents a repository to retrieve and change data related to media projection. */ interface MediaProjectionRepository { /** Switches the task that should be projected. */ suspend fun switchProjectedTask(task: RunningTaskInfo) /** Represents the current [MediaProjectionState]. */ val mediaProjectionState: Flow<MediaProjectionState> }