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

Commit 7964c62c authored by Massimo Carli's avatar Massimo Carli Committed by Android (Google) Code Review
Browse files

Merge "[87/n] Handle Letterbox Surface Lifecycle in SplitScreen" into main

parents c123c27c e74a9f18
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -19,8 +19,9 @@ package com.android.wm.shell.compatui.letterbox
import android.os.IBinder
import android.view.SurfaceControl
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags.appCompatRefactoring
import com.android.window.flags.Flags
import com.android.wm.shell.compatui.letterbox.lifecycle.LetterboxLifecycleController
import com.android.wm.shell.compatui.letterbox.lifecycle.LetterboxLifecycleEventFactory
import com.android.wm.shell.compatui.letterbox.lifecycle.isChangeForALeafTask
@@ -45,7 +46,7 @@ class DelegateLetterboxTransitionObserver(
    }

    init {
        if (appCompatRefactoring()) {
        if (Flags.appCompatRefactoring()) {
            logV("Initializing LetterboxTransitionObserver")
            shellInit.addInitCallback({ transitions.registerObserver(this) }, this)
        }
@@ -62,7 +63,7 @@ class DelegateLetterboxTransitionObserver(
            return
        }
        info.changes.forEach { change ->
            if (change.isChangeForALeafTask() && letterboxLifecycleEventFactory.canHandle(change)) {
            if (taskAllowed(change) && letterboxLifecycleEventFactory.canHandle(change)) {
                letterboxLifecycleEventFactory.createLifecycleEvent(change)?.let { event ->
                    letterboxLifecycleController.onLetterboxLifecycleEvent(
                        event,
@@ -77,4 +78,12 @@ class DelegateLetterboxTransitionObserver(
    private fun logV(msg: String) {
        ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, msg)
    }

    // When the flag is disabled all the changes related to leaf Tasks are skipped. This is because
    // a leaf task surfaces should not be the parent of letterbox surfaces.
    // When the flag is enabled, leaf Tasks are handled to cover the case of split screen when
    // Task in the Change is not a leaf Task but it's still useful to find the actual leaf Task used
    // to identify the right letterbox surfaces. Check [TaskIdResolver] for additional information.
    private fun taskAllowed(change: Change): Boolean =
        Flags.appCompatRefactoringFixMultiwindowTaskHierarchy() || change.isChangeForALeafTask()
}
+7 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.compatui.letterbox.lifecycle

import android.app.TaskInfo
import android.graphics.Rect
import android.view.SurfaceControl
import android.window.TransitionInfo.Change
@@ -74,9 +75,13 @@ fun Change.isActivityChange(): Boolean = activityTransitionInfo != null
/** Returns [true] if the [Change] is related to a translucent container. */
fun Change.isTranslucent() = hasFlags(FLAG_TRANSLUCENT)

/** Returns [true] if the related [Task] is a leaf task. */
val TaskInfo.isALeafTask: Boolean
    get() = appCompatTaskInfo?.isLeafTask ?: false

/**
 * Returns [true] if the Task hosts Activities. This is true if the Change has Activity as target or
 * if task is a leaf task.
 * Returns [true] if the [Task] hosts Activities. This is true if the Change has [Activity] as
 * target or if task is a leaf task.
 */
fun Change.isChangeForALeafTask(): Boolean =
    taskInfo?.appCompatTaskInfo?.isLeafTask ?: isActivityChange()
+53 −13
Original line number Diff line number Diff line
@@ -18,14 +18,19 @@ package com.android.wm.shell.compatui.letterbox.lifecycle

import android.graphics.Rect
import android.window.TransitionInfo.Change
import com.android.window.flags.Flags
import com.android.wm.shell.compatui.letterbox.config.LetterboxDependenciesHelper
import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoRepository
import com.android.wm.shell.compatui.letterbox.state.updateTaskLeafState

/**
 * [LetterboxLifecycleEventFactory] implementation which creates a [LetterboxLifecycleEvent] from a
 * [TransitionInfo.Change] using a [TaskInfo] when present.
 */
class TaskInfoLetterboxLifecycleEventFactory(
    private val letterboxDependenciesHelper: LetterboxDependenciesHelper
    private val letterboxDependenciesHelper: LetterboxDependenciesHelper,
    private val letterboxTaskInfoRepository: LetterboxTaskInfoRepository,
    private val taskIdResolver: TaskIdResolver,
) : LetterboxLifecycleEventFactory {
    override fun canHandle(change: Change): Boolean = change.taskInfo != null

@@ -43,6 +48,40 @@ class TaskInfoLetterboxLifecycleEventFactory(
                letterboxBoundsAbs?.let { absBounds ->
                    Rect(absBounds).apply { offset(-taskBoundsAbs.left, -taskBoundsAbs.top) }
                }
            val shouldSupportInput = letterboxDependenciesHelper.shouldSupportInputSurface(change)
            if (Flags.appCompatRefactoringFixMultiwindowTaskHierarchy()) {
                // Because the [TransitionObserver] is invoked before the [OnTaskAppearedListener]s
                // it's important to store the information about the Task to be reused below for the
                // actual Task resolution given its id and parentId. Only Leaf tasks are stored
                // because they are the only ones with the capability of containing letterbox
                // surfaces.
                letterboxTaskInfoRepository.updateTaskLeafState(ti, change.leash)
                // If the task is not a leaf task the related entry is not present in the
                // Repository. The taskIdResolver will then search for a task which is a direct
                // child. If no Task is found the same id will be used later and the event
                // will be null resulting in a skipped event.
                // If the task is a leaf task the related entry will be present in the Repository
                // and the effectiveTaskId will be the correct taskId to use for the event.
                val effectiveTaskId = taskIdResolver.getLetterboxTaskId(ti)
                // The effectiveTaskId will then be the taskId of a leaf task (using parentId or
                // not) or the id of a missing task (no leaf). In the former case we need to use the
                // related token and leash. In the latter case the method returns null as mentioned
                // above.
                letterboxTaskInfoRepository.find(effectiveTaskId)?.let { item ->
                    return LetterboxLifecycleEvent(
                        type = change.asLetterboxLifecycleEventType(),
                        displayId = ti.displayId,
                        taskId = effectiveTaskId,
                        taskBounds = taskBounds,
                        letterboxBounds = letterboxBounds,
                        containerToken = item.containerToken,
                        taskLeash = item.containerLeash,
                        isBubble = ti.isAppBubble,
                        isTranslucent = change.isTranslucent(),
                        supportsInput = shouldSupportInput,
                    )
                }
            } else {
                return LetterboxLifecycleEvent(
                    type = change.asLetterboxLifecycleEventType(),
                    displayId = ti.displayId,
@@ -53,9 +92,10 @@ class TaskInfoLetterboxLifecycleEventFactory(
                    taskLeash = change.leash,
                    isBubble = ti.isAppBubble,
                    isTranslucent = change.isTranslucent(),
                supportsInput = letterboxDependenciesHelper.shouldSupportInputSurface(change),
                    supportsInput = shouldSupportInput,
                )
            }
        }
        return null
    }
}
+27 −0
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package com.android.wm.shell.compatui.letterbox.state

import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityTaskManager
import android.view.SurfaceControl
import android.window.WindowContainerToken
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.compatui.letterbox.lifecycle.isALeafTask
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
import com.android.wm.shell.repository.GenericRepository
@@ -45,3 +47,28 @@ class LetterboxTaskInfoRepository @Inject constructor() :
            ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", "TaskInfoMemoryRepository", msg)
        }
    )

/**
 * We assume that only leaf Tasks will be present in the [LetterboxTaskInfoRepository]. This method
 * is responsible for keeping this invariant always true.
 */
fun LetterboxTaskInfoRepository.updateTaskLeafState(
    taskInfo: RunningTaskInfo,
    leash: SurfaceControl,
) {
    if (taskInfo.isALeafTask) {
        insert(
            key = taskInfo.taskId,
            item =
                LetterboxTaskInfoState(
                    containerToken = taskInfo.token,
                    containerLeash = leash,
                    parentTaskId = taskInfo.parentTaskId,
                    taskId = taskInfo.taskId,
                ),
            overrideIfPresent = true,
        )
    } else {
        delete(taskInfo.taskId)
    }
}
+13 −14
Original line number Diff line number Diff line
@@ -21,8 +21,9 @@ import android.view.SurfaceControl
import com.android.window.flags.Flags
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTaskOrganizer.TaskAppearedListener
import com.android.wm.shell.ShellTaskOrganizer.TaskInfoChangedListener
import com.android.wm.shell.ShellTaskOrganizer.TaskVanishedListener
import com.android.wm.shell.compatui.letterbox.lifecycle.TaskIdResolver
import com.android.wm.shell.compatui.letterbox.lifecycle.isALeafTask
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.sysui.ShellInit
import javax.inject.Inject
@@ -38,8 +39,7 @@ constructor(
    shellInit: ShellInit,
    shellTaskOrganizer: ShellTaskOrganizer,
    private val letterboxTaskInfoRepository: LetterboxTaskInfoRepository,
    private val taskIdResolver: TaskIdResolver,
) : TaskVanishedListener, TaskAppearedListener {
) : TaskVanishedListener, TaskAppearedListener, TaskInfoChangedListener {

    init {
        if (Flags.appCompatRefactoring()) {
@@ -47,6 +47,9 @@ constructor(
                {
                    shellTaskOrganizer.addTaskAppearedListener(this)
                    shellTaskOrganizer.addTaskVanishedListener(this)
                    if (Flags.appCompatRefactoringFixMultiwindowTaskHierarchy()) {
                        shellTaskOrganizer.addTaskInfoChangedListener(this)
                    }
                },
                this,
            )
@@ -55,17 +58,7 @@ constructor(

    override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
        if (Flags.appCompatRefactoringFixMultiwindowTaskHierarchy()) {
            letterboxTaskInfoRepository.insert(
                key = taskInfo.taskId,
                item =
                    LetterboxTaskInfoState(
                        containerToken = taskInfo.token,
                        containerLeash = leash,
                        parentTaskId = taskInfo.parentTaskId,
                        taskId = taskIdResolver.getLetterboxTaskId(taskInfo),
                    ),
                overrideIfPresent = true,
            )
            letterboxTaskInfoRepository.updateTaskLeafState(taskInfo, leash)
        } else {
            letterboxTaskInfoRepository.insert(
                key = taskInfo.taskId,
@@ -76,6 +69,12 @@ constructor(
        }
    }

    override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
        if (!taskInfo.isALeafTask) {
            letterboxTaskInfoRepository.delete(taskInfo.taskId)
        }
    }

    override fun onTaskVanished(taskInfo: RunningTaskInfo) {
        letterboxTaskInfoRepository.delete(taskInfo.taskId)
    }
Loading