diff options
3 files changed, 212 insertions, 22 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt new file mode 100644 index 000000000000..e88d91f3956c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt @@ -0,0 +1,59 @@ +/* + * 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.view.SurfaceControl +import com.android.wm.shell.dagger.WMSingleton +import javax.inject.Inject + +/** + * Component responsible for the actual creation of the Letterbox surfaces. + */ +@WMSingleton +class LetterboxSurfaceBuilder @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 + } + + fun createSurface( + tx: SurfaceControl.Transaction, + parentLeash: SurfaceControl, + surfaceName: String, + callSite: String, + surfaceBuilder: SurfaceControl.Builder = SurfaceControl.Builder() + ) = surfaceBuilder + .setName(surfaceName) + .setHidden(true) + .setColorLayer() + .setParent(parentLeash) + .setCallsite(callSite) + .build().apply { + tx.setLayer( + this, + TASK_CHILD_LAYER_LETTERBOX_BACKGROUND + ).setColorSpaceAgnostic(this, true) + .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt index d918b87be799..f21a7272287e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt @@ -29,17 +29,10 @@ import javax.inject.Inject */ @WMSingleton class SingleSurfaceLetterboxController @Inject constructor( - private val letterboxConfiguration: LetterboxConfiguration + private val letterboxBuilder: LetterboxSurfaceBuilder ) : 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" } @@ -55,20 +48,12 @@ class SingleSurfaceLetterboxController @Inject constructor( 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()) - } + m[k] = letterboxBuilder.createSurface( + transaction, + parentLeash, + surfaceName = "ShellLetterboxSurface-$key", + callSite = "LetterboxController-createLetterboxSurface" + ) }) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt new file mode 100644 index 000000000000..68d9bf9b926f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt @@ -0,0 +1,146 @@ +/* + * 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.content.Context +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn +import com.android.wm.shell.ShellTestCase +import java.util.function.Consumer +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.verify +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.verification.VerificationMode + +/** + * Tests for [LetterboxSurfaceBuilder]. + * + * Build/Install/Run: + * atest WMShellUnitTests:LetterboxSurfaceBuilderTest + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LetterboxSurfaceBuilderTest : ShellTestCase() { + + @Test + fun `When surface is created mandatory methods are invoked`() { + runTestScenario { r -> + r.invokeBuilder() + + r.checkNameIsSet(expected = true) + r.checkCallSiteIsSet(expected = true) + r.checkSurfaceIsHidden(invoked = true, isHidden = true) + r.checkColorLayerIsSet(expected = true) + r.checkParentLeashIsSet(expected = true) + r.checkSetLayerIsInvoked(expected = true) + r.checkColorSpaceAgnosticIsSet(expected = true, value = true) + r.checkColorIsSetFromLetterboxConfiguration(expected = true) + } + } + + /** + * Runs a test scenario providing a Robot. + */ + fun runTestScenario(consumer: Consumer<LetterboxSurfaceBuilderRobotTest>) { + val robot = LetterboxSurfaceBuilderRobotTest(mContext) + consumer.accept(robot) + } + + class LetterboxSurfaceBuilderRobotTest(val ctx: Context) { + + private val letterboxConfiguration: LetterboxConfiguration + private val letterboxSurfaceBuilder: LetterboxSurfaceBuilder + private val tx: SurfaceControl.Transaction + private val parentLeash: SurfaceControl + private val surfaceBuilder: SurfaceControl.Builder + + companion object { + @JvmStatic + val TEST_SURFACE_NAME = "SurfaceForTest" + + @JvmStatic + val TEST_SURFACE_CALL_SITE = "CallSiteForTest" + } + + init { + letterboxConfiguration = LetterboxConfiguration(ctx) + letterboxSurfaceBuilder = LetterboxSurfaceBuilder(letterboxConfiguration) + tx = org.mockito.kotlin.mock<SurfaceControl.Transaction>() + doReturn(tx).`when`(tx).setLayer(anyOrNull(), anyOrNull()) + doReturn(tx).`when`(tx).setColorSpaceAgnostic(anyOrNull(), anyOrNull()) + parentLeash = org.mockito.kotlin.mock<SurfaceControl>() + surfaceBuilder = SurfaceControl.Builder() + spyOn(surfaceBuilder) + } + + fun invokeBuilder() { + letterboxSurfaceBuilder.createSurface( + tx, + parentLeash, + TEST_SURFACE_NAME, + TEST_SURFACE_CALL_SITE, + surfaceBuilder + ) + } + + fun checkNameIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()) + .setName(TEST_SURFACE_NAME) + } + + fun checkCallSiteIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()) + .setCallsite(TEST_SURFACE_CALL_SITE) + } + + fun checkSurfaceIsHidden(invoked: Boolean, isHidden: Boolean) { + verify(surfaceBuilder, invoked.asMode()) + .setHidden(isHidden) + } + + fun checkColorLayerIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()).setColorLayer() + } + + fun checkParentLeashIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()).setParent(parentLeash) + } + + fun checkSetLayerIsInvoked(expected: Boolean) { + verify(tx, expected.asMode()).setLayer(anyOrNull(), ArgumentMatchers.anyInt()) + } + + fun checkColorSpaceAgnosticIsSet(expected: Boolean, value: Boolean) { + verify(tx, expected.asMode()).setColorSpaceAgnostic(anyOrNull(), eq(value)) + } + + fun checkColorIsSetFromLetterboxConfiguration(expected: Boolean) { + val components = letterboxConfiguration.getBackgroundColorRgbArray() + verify(tx, expected.asMode()).setColor(anyOrNull(), eq(components)) + } + + private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never() + } +} |