Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67bf5285 authored by Chris Göllner's avatar Chris Göllner Committed by Android (Google) Code Review
Browse files

Merge "PSS: Task Switcher - Client side actions implementation" into main

parents 28a2a597 cfed58f4
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -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 =
@@ -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
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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
}
+26 −2
Original line number Diff line number Diff line
@@ -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
@@ -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? =
@@ -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 {
+20 −1
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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 =
@@ -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)
    }

+4 −0
Original line number Diff line number Diff line
@@ -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