Loading core/java/android/app/AppCompatTaskInfo.java +12 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.TaskInfo.PROPERTY_VALUE_UNSET; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; Loading Loading @@ -68,6 +69,14 @@ public class AppCompatTaskInfo implements Parcelable { */ public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET; /** * Contains the top activity bounds when the activity is letterboxed. * It's {@code null} if there's no top activity in the task or it's not letterboxed. */ // TODO(b/379824541) Remove duplicate information. @Nullable public Rect topActivityLetterboxBounds; /** * Stores camera-related app compat information about a particular Task. */ Loading Loading @@ -378,6 +387,7 @@ public class AppCompatTaskInfo implements Parcelable { topActivityLetterboxHeight = source.readInt(); topActivityLetterboxAppWidth = source.readInt(); topActivityLetterboxAppHeight = source.readInt(); topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR); cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR); } Loading @@ -393,6 +403,7 @@ public class AppCompatTaskInfo implements Parcelable { dest.writeInt(topActivityLetterboxHeight); dest.writeInt(topActivityLetterboxAppWidth); dest.writeInt(topActivityLetterboxAppHeight); dest.writeTypedObject(topActivityLetterboxBounds, flags); dest.writeTypedObject(cameraCompatTaskInfo, flags); } Loading @@ -415,6 +426,7 @@ public class AppCompatTaskInfo implements Parcelable { + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled() + " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled() + " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride() + " topActivityLetterboxBounds=" + topActivityLetterboxBounds + " cameraCompatTaskInfo=" + cameraCompatTaskInfo.toString() + "}"; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt +2 −1 Original line number Diff line number Diff line Loading @@ -58,7 +58,8 @@ interface LetterboxController { fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: Transaction, taskBounds: Rect taskBounds: Rect, activityBounds: Rect ) /** Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt +14 −1 Original line number Diff line number Diff line Loading @@ -16,5 +16,18 @@ 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) // Encapsulates the surfaces in the multiple surfaces scenario. data class LetterboxSurfaces( var leftSurface: SurfaceControl? = null, var topSurface: SurfaceControl? = null, var rightSurface: SurfaceControl? = null, var bottomSurface: SurfaceControl? = null ) : Iterable<SurfaceControl?> { override fun iterator() = listOf(leftSurface, topSurface, rightSurface, bottomSurface).iterator() } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt +6 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ class LetterboxTransitionObserver( companion object { @JvmStatic private val TAG = "LetterboxTransitionObserver" @JvmStatic private val EMPTY_BOUNDS = Rect() } init { Loading Loading @@ -86,10 +88,13 @@ class LetterboxTransitionObserver( startTransaction, change.leash ) val activityBounds = ti.appCompatTaskInfo.topActivityLetterboxBounds ?: EMPTY_BOUNDS updateLetterboxSurfaceBounds( key, startTransaction, taskBounds taskBounds, activityBounds ) } updateLetterboxSurfaceVisibility( Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt 0 → 100644 +176 −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 multiple letterbox surfaces when needed. */ @WMSingleton class MultiSurfaceLetterboxController @Inject constructor( private val letterboxBuilder: LetterboxSurfaceBuilder ) : LetterboxController { companion object { @JvmStatic private val TAG = "MultiSurfaceLetterboxController" } private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxSurfaces>() override fun createLetterboxSurface( key: LetterboxKey, transaction: Transaction, parentLeash: SurfaceControl ) { val surfaceBuilderFn = { position: String -> letterboxBuilder.createSurface( transaction, parentLeash, "ShellLetterboxSurface-$key-$position", "MultiSurfaceLetterboxController#createLetterboxSurface" ) } letterboxMap.runOnItem(key, onMissed = { k, m -> m[k] = LetterboxSurfaces( leftSurface = surfaceBuilderFn("Left"), topSurface = surfaceBuilderFn("Top"), rightSurface = surfaceBuilderFn("Right"), bottomSurface = surfaceBuilderFn("Bottom"), ) }) } override fun destroyLetterboxSurface( key: LetterboxKey, transaction: Transaction ) { letterboxMap.runOnItem(key, onFound = { item -> item.forEach { s -> s.remove(transaction) } }) letterboxMap.remove(key) } override fun updateLetterboxSurfaceVisibility( key: LetterboxKey, transaction: Transaction, visible: Boolean ) { letterboxMap.runOnItem(key, onFound = { item -> item.forEach { s -> s.setVisibility(transaction, visible) } }) } override fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: Transaction, taskBounds: Rect, activityBounds: Rect ) { letterboxMap.runOnItem(key, onFound = { item -> item.updateSurfacesBounds(transaction, taskBounds, activityBounds) }) } override fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") } /* * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present. */ private fun MutableMap<LetterboxKey, LetterboxSurfaces>.runOnItem( key: LetterboxKey, onFound: (LetterboxSurfaces) -> Unit = { _ -> }, onMissed: ( LetterboxKey, MutableMap<LetterboxKey, LetterboxSurfaces> ) -> Unit = { _, _ -> } ) { this[key]?.let { return onFound(it) } return onMissed(key, this) } private fun SurfaceControl?.remove( tx: Transaction ) = this?.let { tx.remove(this) } private fun SurfaceControl?.setVisibility( tx: Transaction, visible: Boolean ) = this?.let { tx.setVisibility(this, visible) } private fun Transaction.moveAndCrop( surface: SurfaceControl, rect: Rect ): Transaction = setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) .setWindowCrop( surface, rect.width(), rect.height() ) private fun LetterboxSurfaces.updateSurfacesBounds( tx: Transaction, taskBounds: Rect, activityBounds: Rect ) { // Update the bounds depending on the activity position. leftSurface?.let { s -> tx.moveAndCrop( s, Rect(taskBounds.left, taskBounds.top, activityBounds.left, taskBounds.bottom) ) } rightSurface?.let { s -> tx.moveAndCrop( s, Rect(activityBounds.right, taskBounds.top, taskBounds.right, taskBounds.bottom) ) } topSurface?.let { s -> tx.moveAndCrop( s, Rect(taskBounds.left, taskBounds.top, taskBounds.right, activityBounds.top) ) } bottomSurface?.let { s -> tx.moveAndCrop( s, Rect(taskBounds.left, activityBounds.bottom, taskBounds.right, taskBounds.bottom) ) } } } Loading
core/java/android/app/AppCompatTaskInfo.java +12 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.TaskInfo.PROPERTY_VALUE_UNSET; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; Loading Loading @@ -68,6 +69,14 @@ public class AppCompatTaskInfo implements Parcelable { */ public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET; /** * Contains the top activity bounds when the activity is letterboxed. * It's {@code null} if there's no top activity in the task or it's not letterboxed. */ // TODO(b/379824541) Remove duplicate information. @Nullable public Rect topActivityLetterboxBounds; /** * Stores camera-related app compat information about a particular Task. */ Loading Loading @@ -378,6 +387,7 @@ public class AppCompatTaskInfo implements Parcelable { topActivityLetterboxHeight = source.readInt(); topActivityLetterboxAppWidth = source.readInt(); topActivityLetterboxAppHeight = source.readInt(); topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR); cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR); } Loading @@ -393,6 +403,7 @@ public class AppCompatTaskInfo implements Parcelable { dest.writeInt(topActivityLetterboxHeight); dest.writeInt(topActivityLetterboxAppWidth); dest.writeInt(topActivityLetterboxAppHeight); dest.writeTypedObject(topActivityLetterboxBounds, flags); dest.writeTypedObject(cameraCompatTaskInfo, flags); } Loading @@ -415,6 +426,7 @@ public class AppCompatTaskInfo implements Parcelable { + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled() + " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled() + " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride() + " topActivityLetterboxBounds=" + topActivityLetterboxBounds + " cameraCompatTaskInfo=" + cameraCompatTaskInfo.toString() + "}"; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt +2 −1 Original line number Diff line number Diff line Loading @@ -58,7 +58,8 @@ interface LetterboxController { fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: Transaction, taskBounds: Rect taskBounds: Rect, activityBounds: Rect ) /** Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt +14 −1 Original line number Diff line number Diff line Loading @@ -16,5 +16,18 @@ 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) // Encapsulates the surfaces in the multiple surfaces scenario. data class LetterboxSurfaces( var leftSurface: SurfaceControl? = null, var topSurface: SurfaceControl? = null, var rightSurface: SurfaceControl? = null, var bottomSurface: SurfaceControl? = null ) : Iterable<SurfaceControl?> { override fun iterator() = listOf(leftSurface, topSurface, rightSurface, bottomSurface).iterator() }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt +6 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ class LetterboxTransitionObserver( companion object { @JvmStatic private val TAG = "LetterboxTransitionObserver" @JvmStatic private val EMPTY_BOUNDS = Rect() } init { Loading Loading @@ -86,10 +88,13 @@ class LetterboxTransitionObserver( startTransaction, change.leash ) val activityBounds = ti.appCompatTaskInfo.topActivityLetterboxBounds ?: EMPTY_BOUNDS updateLetterboxSurfaceBounds( key, startTransaction, taskBounds taskBounds, activityBounds ) } updateLetterboxSurfaceVisibility( Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt 0 → 100644 +176 −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 multiple letterbox surfaces when needed. */ @WMSingleton class MultiSurfaceLetterboxController @Inject constructor( private val letterboxBuilder: LetterboxSurfaceBuilder ) : LetterboxController { companion object { @JvmStatic private val TAG = "MultiSurfaceLetterboxController" } private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxSurfaces>() override fun createLetterboxSurface( key: LetterboxKey, transaction: Transaction, parentLeash: SurfaceControl ) { val surfaceBuilderFn = { position: String -> letterboxBuilder.createSurface( transaction, parentLeash, "ShellLetterboxSurface-$key-$position", "MultiSurfaceLetterboxController#createLetterboxSurface" ) } letterboxMap.runOnItem(key, onMissed = { k, m -> m[k] = LetterboxSurfaces( leftSurface = surfaceBuilderFn("Left"), topSurface = surfaceBuilderFn("Top"), rightSurface = surfaceBuilderFn("Right"), bottomSurface = surfaceBuilderFn("Bottom"), ) }) } override fun destroyLetterboxSurface( key: LetterboxKey, transaction: Transaction ) { letterboxMap.runOnItem(key, onFound = { item -> item.forEach { s -> s.remove(transaction) } }) letterboxMap.remove(key) } override fun updateLetterboxSurfaceVisibility( key: LetterboxKey, transaction: Transaction, visible: Boolean ) { letterboxMap.runOnItem(key, onFound = { item -> item.forEach { s -> s.setVisibility(transaction, visible) } }) } override fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: Transaction, taskBounds: Rect, activityBounds: Rect ) { letterboxMap.runOnItem(key, onFound = { item -> item.updateSurfacesBounds(transaction, taskBounds, activityBounds) }) } override fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") } /* * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present. */ private fun MutableMap<LetterboxKey, LetterboxSurfaces>.runOnItem( key: LetterboxKey, onFound: (LetterboxSurfaces) -> Unit = { _ -> }, onMissed: ( LetterboxKey, MutableMap<LetterboxKey, LetterboxSurfaces> ) -> Unit = { _, _ -> } ) { this[key]?.let { return onFound(it) } return onMissed(key, this) } private fun SurfaceControl?.remove( tx: Transaction ) = this?.let { tx.remove(this) } private fun SurfaceControl?.setVisibility( tx: Transaction, visible: Boolean ) = this?.let { tx.setVisibility(this, visible) } private fun Transaction.moveAndCrop( surface: SurfaceControl, rect: Rect ): Transaction = setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) .setWindowCrop( surface, rect.width(), rect.height() ) private fun LetterboxSurfaces.updateSurfacesBounds( tx: Transaction, taskBounds: Rect, activityBounds: Rect ) { // Update the bounds depending on the activity position. leftSurface?.let { s -> tx.moveAndCrop( s, Rect(taskBounds.left, taskBounds.top, activityBounds.left, taskBounds.bottom) ) } rightSurface?.let { s -> tx.moveAndCrop( s, Rect(activityBounds.right, taskBounds.top, taskBounds.right, taskBounds.bottom) ) } topSurface?.let { s -> tx.moveAndCrop( s, Rect(taskBounds.left, taskBounds.top, taskBounds.right, activityBounds.top) ) } bottomSurface?.let { s -> tx.moveAndCrop( s, Rect(taskBounds.left, activityBounds.bottom, taskBounds.right, taskBounds.bottom) ) } } }