Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt +17 −99 Original line number Diff line number Diff line Loading @@ -18,133 +18,51 @@ package com.android.wm.shell.compatui.letterbox import android.graphics.Rect import android.view.SurfaceControl import com.android.internal.protolog.ProtoLog import com.android.wm.shell.dagger.WMSingleton import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT import javax.inject.Inject import android.view.SurfaceControl.Transaction /** * Component responsible for handling the lifecycle of the letterbox surfaces. * Abstracts the component responsible to handle a single or multiple letterbox surfaces for a * specific [Change]. */ @WMSingleton class LetterboxController @Inject constructor( private val letterboxConfiguration: LetterboxConfiguration ) { companion object { /* * Letterbox surfaces need to stay below the activity layer which is 0. */ // TODO(b/378673153): Consider adding this to [TaskConstants]. @JvmStatic private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 @JvmStatic private val TAG = "LetterboxController" } private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxItem>() interface LetterboxController { /** * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. */ fun createLetterboxSurface( key: LetterboxKey, startTransaction: SurfaceControl.Transaction, transaction: Transaction, parentLeash: SurfaceControl ) { letterboxMap.runOnItem(key, onMissed = { k, m -> m[k] = LetterboxItem( SurfaceControl.Builder() .setName("ShellLetterboxSurface-$key") .setHidden(true) .setColorLayer() .setParent(parentLeash) .setCallsite("LetterboxController-createLetterboxSurface") .build().apply { startTransaction.setLayer( this, TASK_CHILD_LAYER_LETTERBOX_BACKGROUND ).setColorSpaceAgnostic(this, true) .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) } ) }) } /** * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. */ fun destroyLetterboxSurface( key: LetterboxKey, startTransaction: SurfaceControl.Transaction ) { letterboxMap.runOnItem(key, onFound = { item -> item.fullWindowSurface?.run { startTransaction.remove(this) } }) letterboxMap.remove(key) } transaction: Transaction ) /** * Invoked to show/hide the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceVisibility( key: LetterboxKey, startTransaction: SurfaceControl.Transaction, visible: Boolean = true ) { letterboxMap.runOnItem(key, onFound = { item -> item.fullWindowSurface?.run { startTransaction.setVisibility(this, visible) } }) } transaction: Transaction, visible: Boolean ) /** * Updates the bounds for the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceBounds( key: LetterboxKey, startTransaction: SurfaceControl.Transaction, bounds: Rect ) { letterboxMap.runOnItem(key, onFound = { item -> item.fullWindowSurface?.run { startTransaction.moveAndCrop(this, bounds) } }) } transaction: Transaction, taskBounds: Rect ) /* * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present. /** * Utility method to dump the current state. */ private fun MutableMap<LetterboxKey, LetterboxItem>.runOnItem( key: LetterboxKey, onFound: (LetterboxItem) -> Unit = { _ -> }, onMissed: ( LetterboxKey, MutableMap<LetterboxKey, LetterboxItem> ) -> Unit = { _, _ -> } ) { this[key]?.let { return onFound(it) } return onMissed(key, this) } fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") } private fun SurfaceControl.Transaction.moveAndCrop( surface: SurfaceControl, rect: Rect ): SurfaceControl.Transaction = setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) .setWindowCrop( surface, rect.width(), rect.height() ) fun dump() } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt +1 −6 Original line number Diff line number Diff line Loading @@ -16,10 +16,5 @@ package com.android.wm.shell.compatui.letterbox import android.view.SurfaceControl // The key to use for identify the letterbox sessions. data class LetterboxKey(val displayId: Int, val taskId: Int) No newline at end of file // Encapsulate the objects for the specific letterbox session. data class LetterboxItem(val fullWindowSurface: SurfaceControl?) No newline at end of file libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt +32 −30 Original line number Diff line number Diff line Loading @@ -43,12 +43,7 @@ class LetterboxTransitionObserver( init { if (appCompatRefactoring()) { ProtoLog.v( WM_SHELL_APP_COMPAT, "%s: %s", TAG, "Initializing LetterboxTransitionObserver" ) logV("Initializing LetterboxTransitionObserver") shellInit.addInitCallback({ transitions.registerObserver(this) }, this) Loading @@ -69,38 +64,45 @@ class LetterboxTransitionObserver( for (change in info.changes) { change.taskInfo?.let { ti -> val key = LetterboxKey(ti.displayId, ti.taskId) val taskBounds = Rect( change.endRelOffset.x, change.endRelOffset.y, change.endAbsBounds.width(), change.endAbsBounds.height() ) with(letterboxController) { if (isClosingType(change.mode)) { letterboxController.destroyLetterboxSurface( destroyLetterboxSurface( key, startTransaction ) } else { val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed if (isTopActivityLetterboxed) { letterboxController.createLetterboxSurface( createLetterboxSurface( key, startTransaction, change.leash ) letterboxController.updateLetterboxSurfaceBounds( updateLetterboxSurfaceBounds( key, startTransaction, Rect( change.endRelOffset.x, change.endRelOffset.y, change.endAbsBounds.width(), change.endAbsBounds.height() ) taskBounds ) } letterboxController.updateLetterboxSurfaceVisibility( updateLetterboxSurfaceVisibility( key, startTransaction, isTopActivityLetterboxed ) } letterboxController.dump() dump() } } } } private fun logV(msg: String) { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, msg) } } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.compatui.letterbox import android.graphics.Rect import android.view.SurfaceControl import android.view.SurfaceControl.Transaction import com.android.internal.protolog.ProtoLog import com.android.wm.shell.dagger.WMSingleton import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT import javax.inject.Inject /** * Component responsible for handling the lifecycle of a single letterbox surface. */ @WMSingleton class SingleSurfaceLetterboxController @Inject constructor( private val letterboxConfiguration: LetterboxConfiguration ) : LetterboxController { companion object { /* * Letterbox surfaces need to stay below the activity layer which is 0. */ // TODO(b/378673153): Consider adding this to [TaskConstants]. @JvmStatic private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 @JvmStatic private val TAG = "LetterboxController" } private val letterboxMap = mutableMapOf<LetterboxKey, SurfaceControl>() /** * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. */ override fun createLetterboxSurface( key: LetterboxKey, transaction: Transaction, parentLeash: SurfaceControl ) { letterboxMap.runOnItem(key, onMissed = { k, m -> m[k] = SurfaceControl.Builder() .setName("ShellLetterboxSurface-$key") .setHidden(true) .setColorLayer() .setParent(parentLeash) .setCallsite("LetterboxController-createLetterboxSurface") .build().apply { transaction.setLayer( this, TASK_CHILD_LAYER_LETTERBOX_BACKGROUND ).setColorSpaceAgnostic(this, true) .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) } }) } /** * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. */ override fun destroyLetterboxSurface( key: LetterboxKey, transaction: Transaction ) { letterboxMap.runOnItem(key, onFound = { item -> item.run { transaction.remove(this) } }) letterboxMap.remove(key) } /** * Invoked to show/hide the letterbox surfaces for given displayId/taskId. */ override fun updateLetterboxSurfaceVisibility( key: LetterboxKey, transaction: Transaction, visible: Boolean ) { letterboxMap.runOnItem(key, onFound = { item -> item.run { transaction.setVisibility(this, visible) } }) } /** * Updates the bounds for the letterbox surfaces for given displayId/taskId. */ override fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: Transaction, taskBounds: Rect ) { letterboxMap.runOnItem(key, onFound = { item -> item.run { transaction.moveAndCrop(this, taskBounds) } }) } override fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") } /* * Executes [onFound] on the [SurfaceControl] if present or [onMissed] if not present. */ private fun MutableMap<LetterboxKey, SurfaceControl>.runOnItem( key: LetterboxKey, onFound: (SurfaceControl) -> Unit = { _ -> }, onMissed: ( LetterboxKey, MutableMap<LetterboxKey, SurfaceControl> ) -> Unit = { _, _ -> } ) { this[key]?.let { return onFound(it) } return onMissed(key, this) } private fun Transaction.moveAndCrop( surface: SurfaceControl, rect: Rect ): Transaction = setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) .setWindowCrop( surface, rect.width(), rect.height() ) } libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +6 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler; import com.android.wm.shell.compatui.letterbox.LetterboxController; import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver; import com.android.wm.shell.compatui.letterbox.SingleSurfaceLetterboxController; import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; Loading Loading @@ -1318,4 +1319,9 @@ public abstract class WMShellModule { ) { return new LetterboxTransitionObserver(shellInit, transitions, letterboxController); } @WMSingleton @Binds abstract LetterboxController bindsLetterboxController( SingleSurfaceLetterboxController letterboxController); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt +17 −99 Original line number Diff line number Diff line Loading @@ -18,133 +18,51 @@ package com.android.wm.shell.compatui.letterbox import android.graphics.Rect import android.view.SurfaceControl import com.android.internal.protolog.ProtoLog import com.android.wm.shell.dagger.WMSingleton import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT import javax.inject.Inject import android.view.SurfaceControl.Transaction /** * Component responsible for handling the lifecycle of the letterbox surfaces. * Abstracts the component responsible to handle a single or multiple letterbox surfaces for a * specific [Change]. */ @WMSingleton class LetterboxController @Inject constructor( private val letterboxConfiguration: LetterboxConfiguration ) { companion object { /* * Letterbox surfaces need to stay below the activity layer which is 0. */ // TODO(b/378673153): Consider adding this to [TaskConstants]. @JvmStatic private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 @JvmStatic private val TAG = "LetterboxController" } private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxItem>() interface LetterboxController { /** * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. */ fun createLetterboxSurface( key: LetterboxKey, startTransaction: SurfaceControl.Transaction, transaction: Transaction, parentLeash: SurfaceControl ) { letterboxMap.runOnItem(key, onMissed = { k, m -> m[k] = LetterboxItem( SurfaceControl.Builder() .setName("ShellLetterboxSurface-$key") .setHidden(true) .setColorLayer() .setParent(parentLeash) .setCallsite("LetterboxController-createLetterboxSurface") .build().apply { startTransaction.setLayer( this, TASK_CHILD_LAYER_LETTERBOX_BACKGROUND ).setColorSpaceAgnostic(this, true) .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) } ) }) } /** * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. */ fun destroyLetterboxSurface( key: LetterboxKey, startTransaction: SurfaceControl.Transaction ) { letterboxMap.runOnItem(key, onFound = { item -> item.fullWindowSurface?.run { startTransaction.remove(this) } }) letterboxMap.remove(key) } transaction: Transaction ) /** * Invoked to show/hide the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceVisibility( key: LetterboxKey, startTransaction: SurfaceControl.Transaction, visible: Boolean = true ) { letterboxMap.runOnItem(key, onFound = { item -> item.fullWindowSurface?.run { startTransaction.setVisibility(this, visible) } }) } transaction: Transaction, visible: Boolean ) /** * Updates the bounds for the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceBounds( key: LetterboxKey, startTransaction: SurfaceControl.Transaction, bounds: Rect ) { letterboxMap.runOnItem(key, onFound = { item -> item.fullWindowSurface?.run { startTransaction.moveAndCrop(this, bounds) } }) } transaction: Transaction, taskBounds: Rect ) /* * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present. /** * Utility method to dump the current state. */ private fun MutableMap<LetterboxKey, LetterboxItem>.runOnItem( key: LetterboxKey, onFound: (LetterboxItem) -> Unit = { _ -> }, onMissed: ( LetterboxKey, MutableMap<LetterboxKey, LetterboxItem> ) -> Unit = { _, _ -> } ) { this[key]?.let { return onFound(it) } return onMissed(key, this) } fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") } private fun SurfaceControl.Transaction.moveAndCrop( surface: SurfaceControl, rect: Rect ): SurfaceControl.Transaction = setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) .setWindowCrop( surface, rect.width(), rect.height() ) fun dump() }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt +1 −6 Original line number Diff line number Diff line Loading @@ -16,10 +16,5 @@ package com.android.wm.shell.compatui.letterbox import android.view.SurfaceControl // The key to use for identify the letterbox sessions. data class LetterboxKey(val displayId: Int, val taskId: Int) No newline at end of file // Encapsulate the objects for the specific letterbox session. data class LetterboxItem(val fullWindowSurface: SurfaceControl?) No newline at end of file
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt +32 −30 Original line number Diff line number Diff line Loading @@ -43,12 +43,7 @@ class LetterboxTransitionObserver( init { if (appCompatRefactoring()) { ProtoLog.v( WM_SHELL_APP_COMPAT, "%s: %s", TAG, "Initializing LetterboxTransitionObserver" ) logV("Initializing LetterboxTransitionObserver") shellInit.addInitCallback({ transitions.registerObserver(this) }, this) Loading @@ -69,38 +64,45 @@ class LetterboxTransitionObserver( for (change in info.changes) { change.taskInfo?.let { ti -> val key = LetterboxKey(ti.displayId, ti.taskId) val taskBounds = Rect( change.endRelOffset.x, change.endRelOffset.y, change.endAbsBounds.width(), change.endAbsBounds.height() ) with(letterboxController) { if (isClosingType(change.mode)) { letterboxController.destroyLetterboxSurface( destroyLetterboxSurface( key, startTransaction ) } else { val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed if (isTopActivityLetterboxed) { letterboxController.createLetterboxSurface( createLetterboxSurface( key, startTransaction, change.leash ) letterboxController.updateLetterboxSurfaceBounds( updateLetterboxSurfaceBounds( key, startTransaction, Rect( change.endRelOffset.x, change.endRelOffset.y, change.endAbsBounds.width(), change.endAbsBounds.height() ) taskBounds ) } letterboxController.updateLetterboxSurfaceVisibility( updateLetterboxSurfaceVisibility( key, startTransaction, isTopActivityLetterboxed ) } letterboxController.dump() dump() } } } } private fun logV(msg: String) { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, msg) } }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.compatui.letterbox import android.graphics.Rect import android.view.SurfaceControl import android.view.SurfaceControl.Transaction import com.android.internal.protolog.ProtoLog import com.android.wm.shell.dagger.WMSingleton import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT import javax.inject.Inject /** * Component responsible for handling the lifecycle of a single letterbox surface. */ @WMSingleton class SingleSurfaceLetterboxController @Inject constructor( private val letterboxConfiguration: LetterboxConfiguration ) : LetterboxController { companion object { /* * Letterbox surfaces need to stay below the activity layer which is 0. */ // TODO(b/378673153): Consider adding this to [TaskConstants]. @JvmStatic private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 @JvmStatic private val TAG = "LetterboxController" } private val letterboxMap = mutableMapOf<LetterboxKey, SurfaceControl>() /** * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. */ override fun createLetterboxSurface( key: LetterboxKey, transaction: Transaction, parentLeash: SurfaceControl ) { letterboxMap.runOnItem(key, onMissed = { k, m -> m[k] = SurfaceControl.Builder() .setName("ShellLetterboxSurface-$key") .setHidden(true) .setColorLayer() .setParent(parentLeash) .setCallsite("LetterboxController-createLetterboxSurface") .build().apply { transaction.setLayer( this, TASK_CHILD_LAYER_LETTERBOX_BACKGROUND ).setColorSpaceAgnostic(this, true) .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) } }) } /** * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. */ override fun destroyLetterboxSurface( key: LetterboxKey, transaction: Transaction ) { letterboxMap.runOnItem(key, onFound = { item -> item.run { transaction.remove(this) } }) letterboxMap.remove(key) } /** * Invoked to show/hide the letterbox surfaces for given displayId/taskId. */ override fun updateLetterboxSurfaceVisibility( key: LetterboxKey, transaction: Transaction, visible: Boolean ) { letterboxMap.runOnItem(key, onFound = { item -> item.run { transaction.setVisibility(this, visible) } }) } /** * Updates the bounds for the letterbox surfaces for given displayId/taskId. */ override fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: Transaction, taskBounds: Rect ) { letterboxMap.runOnItem(key, onFound = { item -> item.run { transaction.moveAndCrop(this, taskBounds) } }) } override fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") } /* * Executes [onFound] on the [SurfaceControl] if present or [onMissed] if not present. */ private fun MutableMap<LetterboxKey, SurfaceControl>.runOnItem( key: LetterboxKey, onFound: (SurfaceControl) -> Unit = { _ -> }, onMissed: ( LetterboxKey, MutableMap<LetterboxKey, SurfaceControl> ) -> Unit = { _, _ -> } ) { this[key]?.let { return onFound(it) } return onMissed(key, this) } private fun Transaction.moveAndCrop( surface: SurfaceControl, rect: Rect ): Transaction = setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) .setWindowCrop( surface, rect.width(), rect.height() ) }
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +6 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler; import com.android.wm.shell.compatui.letterbox.LetterboxController; import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver; import com.android.wm.shell.compatui.letterbox.SingleSurfaceLetterboxController; import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; Loading Loading @@ -1318,4 +1319,9 @@ public abstract class WMShellModule { ) { return new LetterboxTransitionObserver(shellInit, transitions, letterboxController); } @WMSingleton @Binds abstract LetterboxController bindsLetterboxController( SingleSurfaceLetterboxController letterboxController); }