Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/DelegateLetterboxTransitionObserver.kt +12 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -45,7 +46,7 @@ class DelegateLetterboxTransitionObserver( } init { if (appCompatRefactoring()) { if (Flags.appCompatRefactoring()) { logV("Initializing LetterboxTransitionObserver") shellInit.addInitCallback({ transitions.registerObserver(this) }, this) } Loading @@ -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, Loading @@ -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() } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/lifecycle/LetterboxLifecycleEvent.kt +7 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/lifecycle/TaskInfoLetterboxLifecycleEventFactory.kt +53 −13 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -53,9 +92,10 @@ class TaskInfoLetterboxLifecycleEventFactory( taskLeash = change.leash, isBubble = ti.isAppBubble, isTranslucent = change.isTranslucent(), supportsInput = letterboxDependenciesHelper.shouldSupportInputSurface(change), supportsInput = shouldSupportInput, ) } } return null } } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/state/LetterboxTaskInfoRepository.kt +27 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) } } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/state/LetterboxTaskListenerAdapter.kt +13 −14 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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()) { Loading @@ -47,6 +47,9 @@ constructor( { shellTaskOrganizer.addTaskAppearedListener(this) shellTaskOrganizer.addTaskVanishedListener(this) if (Flags.appCompatRefactoringFixMultiwindowTaskHierarchy()) { shellTaskOrganizer.addTaskInfoChangedListener(this) } }, this, ) Loading @@ -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, Loading @@ -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 Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/DelegateLetterboxTransitionObserver.kt +12 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -45,7 +46,7 @@ class DelegateLetterboxTransitionObserver( } init { if (appCompatRefactoring()) { if (Flags.appCompatRefactoring()) { logV("Initializing LetterboxTransitionObserver") shellInit.addInitCallback({ transitions.registerObserver(this) }, this) } Loading @@ -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, Loading @@ -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() }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/lifecycle/LetterboxLifecycleEvent.kt +7 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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()
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/lifecycle/TaskInfoLetterboxLifecycleEventFactory.kt +53 −13 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading @@ -53,9 +92,10 @@ class TaskInfoLetterboxLifecycleEventFactory( taskLeash = change.leash, isBubble = ti.isAppBubble, isTranslucent = change.isTranslucent(), supportsInput = letterboxDependenciesHelper.shouldSupportInputSurface(change), supportsInput = shouldSupportInput, ) } } return null } }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/state/LetterboxTaskInfoRepository.kt +27 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) } }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/state/LetterboxTaskListenerAdapter.kt +13 −14 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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()) { Loading @@ -47,6 +47,9 @@ constructor( { shellTaskOrganizer.addTaskAppearedListener(this) shellTaskOrganizer.addTaskVanishedListener(this) if (Flags.appCompatRefactoringFixMultiwindowTaskHierarchy()) { shellTaskOrganizer.addTaskInfoChangedListener(this) } }, this, ) Loading @@ -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, Loading @@ -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