Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersLetterboxController.kt 0 → 100644 +99 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.roundedcorners import android.graphics.Rect import android.view.SurfaceControl import android.window.WindowContainerToken import com.android.internal.protolog.ProtoLog import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.compatui.letterbox.LetterboxController import com.android.wm.shell.compatui.letterbox.LetterboxKey import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoRepository import com.android.wm.shell.dagger.WMSingleton import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT import com.android.wm.shell.shared.annotations.ShellMainThread import javax.inject.Inject /** [LetterboxController] implementation to handle rounded corners for Letterbox in Shell. */ @WMSingleton class RoundedCornersLetterboxController @Inject constructor( @ShellMainThread private val animExecutor: ShellExecutor, private val roundedCornersSurfaceBuilder: RoundedCornersSurfaceBuilder, private val taskRepository: LetterboxTaskInfoRepository, ) : LetterboxController { companion object { @JvmStatic private val TAG = "RoundedCornersLetterboxController" } private val roundedCornersMap = mutableMapOf<Int, RoundedCornersSurface>() override fun createLetterboxSurface( key: LetterboxKey, transaction: SurfaceControl.Transaction, parentLeash: SurfaceControl, token: WindowContainerToken?, ) { roundedCornersMap.runOnItem( key.taskId, onMissed = { k, m -> taskRepository.find(key.taskId)?.let { item -> m[k] = roundedCornersSurfaceBuilder.create(item.configuration, parentLeash) } }, ) } override fun destroyLetterboxSurface( key: LetterboxKey, transaction: SurfaceControl.Transaction, ) { roundedCornersMap.runOnItem(key.taskId, onFound = { item -> item.release() }) roundedCornersMap.remove(key.taskId) } override fun updateLetterboxSurfaceVisibility( key: LetterboxKey, transaction: SurfaceControl.Transaction, visible: Boolean, ) { roundedCornersMap.runOnItem( key.taskId, onFound = { item -> item.setCornersVisibility(animExecutor, visible, immediate = true) }, ) } override fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: SurfaceControl.Transaction, taskBounds: Rect, activityBounds: Rect, ) { roundedCornersMap.runOnItem( key.taskId, onFound = { item -> item.updateSurfaceBounds(activityBounds) }, ) } override fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "$roundedCornersMap") } } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersSurface.kt +2 −3 Original line number Original line Diff line number Diff line Loading @@ -36,7 +36,6 @@ import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY import android.view.WindowlessWindowManager import android.view.WindowlessWindowManager import android.widget.FrameLayout import android.widget.FrameLayout import android.window.TaskConstants import android.window.TaskConstants import android.window.TaskConstants.TASK_CHILD_SHELL_LAYER_LETTERBOX_ROUNDED_CORNERS import com.android.wm.shell.R import com.android.wm.shell.R import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.SyncTransactionQueue Loading @@ -56,7 +55,7 @@ class RoundedCornersSurface( private val cornersFactory: LetterboxRoundedCornersDrawableFactory, private val cornersFactory: LetterboxRoundedCornersDrawableFactory, private val letterboxConfiguration: LetterboxConfiguration, private val letterboxConfiguration: LetterboxConfiguration, private val surfaceBuilderSupplier: SurfaceBuilderSupplier, private val surfaceBuilderSupplier: SurfaceBuilderSupplier, ) : WindowlessWindowManager(config, parentSurface, /* hostInputToken */ null) { ) : WindowlessWindowManager(config, parentSurface, null) { private var viewHost: SurfaceControlViewHost? = null private var viewHost: SurfaceControlViewHost? = null Loading Loading @@ -90,7 +89,7 @@ class RoundedCornersSurface( surfaceBuilderSupplier surfaceBuilderSupplier .get() .get() .setContainerLayer() .setContainerLayer() .setName(className + "Leash") .setName("LetterboxRoundedCornersParentSurface") .setHidden(false) .setHidden(false) .setParent(parentSurface) .setParent(parentSurface) .setCallsite("$className#attachToParentSurface") .setCallsite("$className#attachToParentSurface") Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersSurfaceBuilder.kt 0 → 100644 +50 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.roundedcorners import android.content.Context import android.content.res.Configuration import android.view.SurfaceControl import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.suppliers.SurfaceBuilderSupplier import com.android.wm.shell.compatui.letterbox.LetterboxConfiguration import com.android.wm.shell.dagger.WMSingleton import javax.inject.Inject /** Component responsible for the actual creation of the RoundedCorners surfaces. */ @WMSingleton class RoundedCornersSurfaceBuilder @Inject constructor( private val ctx: Context, private val syncQueue: SyncTransactionQueue, private val surfaceBuilderSupplier: SurfaceBuilderSupplier, private val roundedCornersFactory: LetterboxRoundedCornersDrawableFactory, private val letterboxConfiguration: LetterboxConfiguration, ) { fun create(conf: Configuration, parentLeash: SurfaceControl): RoundedCornersSurface = RoundedCornersSurface( ctx, conf, syncQueue, parentLeash, roundedCornersFactory, letterboxConfiguration, surfaceBuilderSupplier, ) } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt +1 −1 Original line number Original line Diff line number Diff line Loading @@ -43,7 +43,7 @@ abstract class LetterboxControllerRobotTest { val ANOTHER_TASK_ID = 10 val ANOTHER_TASK_ID = 10 @JvmStatic @JvmStatic private val TOKEN = mock<WindowContainerToken>() val TOKEN = mock<WindowContainerToken>() } } lateinit var letterboxController: LetterboxController lateinit var letterboxController: LetterboxController Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersLetterboxControllerTest.kt 0 → 100644 +206 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.roundedcorners import android.content.res.Configuration import android.graphics.Rect import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.compatui.letterbox.LetterboxController import com.android.wm.shell.compatui.letterbox.LetterboxControllerRobotTest import com.android.wm.shell.compatui.letterbox.LetterboxControllerRobotTest.Companion.ANOTHER_TASK_ID import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoRepository import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoState import java.util.function.Consumer import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify /** * Tests for [RoundedCornersLetterboxController]. * * Build/Install/Run: * atest WMShellUnitTests:RoundedCornersLetterboxControllerTest */ @RunWith(AndroidTestingRunner::class) @SmallTest class RoundedCornersLetterboxControllerTest : ShellTestCase() { @Test fun `Rounded corners are NOT created if configuration is missing from the repository`() { runTestScenario { r -> r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked(times = 0) } } @Test fun `Rounded corners are created if configuration is missing from the repository`() { runTestScenario { r -> r.configureRepository() r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked(times = 1) } } @Test fun `When creation is requested multiple times rounded corners are created once`() { runTestScenario { r -> r.configureRepository() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked(times = 1) } } @Test fun `A different surface is created for every key`() { runTestScenario { r -> r.configureRepository() r.configureRepository(taskId = ANOTHER_TASK_ID) r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest(taskId = ANOTHER_TASK_ID) r.sendCreateSurfaceRequest(taskId = ANOTHER_TASK_ID) r.checkSurfaceBuilderInvoked(times = 2) } } @Test fun `Created surface is removed once`() { runTestScenario { r -> r.configureRepository() r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked() r.sendDestroySurfaceRequest() r.sendDestroySurfaceRequest() r.sendDestroySurfaceRequest() r.checkRoundedCornersSurfaceReleased() } } @Test fun `Only existing surfaces receive visibility update`() { runTestScenario { r -> r.configureRepository() r.configureRepository(taskId = ANOTHER_TASK_ID) r.sendCreateSurfaceRequest() r.sendUpdateSurfaceVisibilityRequest(visible = true) r.sendUpdateSurfaceVisibilityRequest(visible = true, taskId = ANOTHER_TASK_ID) r.checkRoundedCornersVisibilityUpdated(times = 1, expectedVisibility = true) } } @Test fun `Only existing surfaces receive taskBounds update`() { runTestScenario { r -> r.configureRepository() r.sendUpdateSurfaceBoundsRequest( taskBounds = Rect(0, 0, 2000, 1000), activityBounds = Rect(500, 0, 1500, 1000) ) r.checkSurfacePositionUpdated(times = 0) r.checkSurfaceSizeUpdated(times = 0) r.resetTransitionTest() r.sendCreateSurfaceRequest() r.sendUpdateSurfaceBoundsRequest( taskBounds = Rect(0, 0, 2000, 1000), activityBounds = Rect(500, 0, 1500, 1000) ) } } /** * Runs a test scenario providing a Robot. */ fun runTestScenario(consumer: Consumer<RoundedCornersControllerRobotTest>) { consumer.accept(RoundedCornersControllerRobotTest().apply { initController() }) } class RoundedCornersControllerRobotTest : LetterboxControllerRobotTest() { private val letterboxRepository: LetterboxTaskInfoRepository private val roundedCornersSurfaceBuilder: RoundedCornersSurfaceBuilder private val roundedCornersSurface: RoundedCornersSurface private val executor: TestShellExecutor private val testConfiguration: Configuration init { testConfiguration = Configuration() executor = TestShellExecutor() letterboxRepository = LetterboxTaskInfoRepository() roundedCornersSurface = mock<RoundedCornersSurface>() roundedCornersSurfaceBuilder = mock<RoundedCornersSurfaceBuilder>() doReturn(roundedCornersSurface).`when`(roundedCornersSurfaceBuilder).create( any(), any() ) } override fun buildController(): LetterboxController = RoundedCornersLetterboxController( executor, roundedCornersSurfaceBuilder, letterboxRepository ) fun configureRepository(taskId: Int = TASK_ID) { letterboxRepository.insert( taskId, LetterboxTaskInfoState(TOKEN, parentLeash, configuration = testConfiguration) ) } fun checkSurfaceBuilderInvoked(times: Int = 1) { verify(roundedCornersSurfaceBuilder, times(times)).create( eq(testConfiguration), eq(parentLeash) ) } fun checkRoundedCornersVisibilityUpdated(times: Int = 1, expectedVisibility: Boolean) { verify(roundedCornersSurface, times(times)).setCornersVisibility( any(), eq(expectedVisibility), eq(true) ) } fun checkRoundedCornersSurfaceReleased(times: Int = 1) { verify(roundedCornersSurface, times(times)).release() } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersLetterboxController.kt 0 → 100644 +99 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.roundedcorners import android.graphics.Rect import android.view.SurfaceControl import android.window.WindowContainerToken import com.android.internal.protolog.ProtoLog import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.compatui.letterbox.LetterboxController import com.android.wm.shell.compatui.letterbox.LetterboxKey import com.android.wm.shell.compatui.letterbox.LetterboxUtils.Maps.runOnItem import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoRepository import com.android.wm.shell.dagger.WMSingleton import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT import com.android.wm.shell.shared.annotations.ShellMainThread import javax.inject.Inject /** [LetterboxController] implementation to handle rounded corners for Letterbox in Shell. */ @WMSingleton class RoundedCornersLetterboxController @Inject constructor( @ShellMainThread private val animExecutor: ShellExecutor, private val roundedCornersSurfaceBuilder: RoundedCornersSurfaceBuilder, private val taskRepository: LetterboxTaskInfoRepository, ) : LetterboxController { companion object { @JvmStatic private val TAG = "RoundedCornersLetterboxController" } private val roundedCornersMap = mutableMapOf<Int, RoundedCornersSurface>() override fun createLetterboxSurface( key: LetterboxKey, transaction: SurfaceControl.Transaction, parentLeash: SurfaceControl, token: WindowContainerToken?, ) { roundedCornersMap.runOnItem( key.taskId, onMissed = { k, m -> taskRepository.find(key.taskId)?.let { item -> m[k] = roundedCornersSurfaceBuilder.create(item.configuration, parentLeash) } }, ) } override fun destroyLetterboxSurface( key: LetterboxKey, transaction: SurfaceControl.Transaction, ) { roundedCornersMap.runOnItem(key.taskId, onFound = { item -> item.release() }) roundedCornersMap.remove(key.taskId) } override fun updateLetterboxSurfaceVisibility( key: LetterboxKey, transaction: SurfaceControl.Transaction, visible: Boolean, ) { roundedCornersMap.runOnItem( key.taskId, onFound = { item -> item.setCornersVisibility(animExecutor, visible, immediate = true) }, ) } override fun updateLetterboxSurfaceBounds( key: LetterboxKey, transaction: SurfaceControl.Transaction, taskBounds: Rect, activityBounds: Rect, ) { roundedCornersMap.runOnItem( key.taskId, onFound = { item -> item.updateSurfaceBounds(activityBounds) }, ) } override fun dump() { ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "$roundedCornersMap") } }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersSurface.kt +2 −3 Original line number Original line Diff line number Diff line Loading @@ -36,7 +36,6 @@ import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY import android.view.WindowlessWindowManager import android.view.WindowlessWindowManager import android.widget.FrameLayout import android.widget.FrameLayout import android.window.TaskConstants import android.window.TaskConstants import android.window.TaskConstants.TASK_CHILD_SHELL_LAYER_LETTERBOX_ROUNDED_CORNERS import com.android.wm.shell.R import com.android.wm.shell.R import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.SyncTransactionQueue Loading @@ -56,7 +55,7 @@ class RoundedCornersSurface( private val cornersFactory: LetterboxRoundedCornersDrawableFactory, private val cornersFactory: LetterboxRoundedCornersDrawableFactory, private val letterboxConfiguration: LetterboxConfiguration, private val letterboxConfiguration: LetterboxConfiguration, private val surfaceBuilderSupplier: SurfaceBuilderSupplier, private val surfaceBuilderSupplier: SurfaceBuilderSupplier, ) : WindowlessWindowManager(config, parentSurface, /* hostInputToken */ null) { ) : WindowlessWindowManager(config, parentSurface, null) { private var viewHost: SurfaceControlViewHost? = null private var viewHost: SurfaceControlViewHost? = null Loading Loading @@ -90,7 +89,7 @@ class RoundedCornersSurface( surfaceBuilderSupplier surfaceBuilderSupplier .get() .get() .setContainerLayer() .setContainerLayer() .setName(className + "Leash") .setName("LetterboxRoundedCornersParentSurface") .setHidden(false) .setHidden(false) .setParent(parentSurface) .setParent(parentSurface) .setCallsite("$className#attachToParentSurface") .setCallsite("$className#attachToParentSurface") Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersSurfaceBuilder.kt 0 → 100644 +50 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.roundedcorners import android.content.Context import android.content.res.Configuration import android.view.SurfaceControl import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.suppliers.SurfaceBuilderSupplier import com.android.wm.shell.compatui.letterbox.LetterboxConfiguration import com.android.wm.shell.dagger.WMSingleton import javax.inject.Inject /** Component responsible for the actual creation of the RoundedCorners surfaces. */ @WMSingleton class RoundedCornersSurfaceBuilder @Inject constructor( private val ctx: Context, private val syncQueue: SyncTransactionQueue, private val surfaceBuilderSupplier: SurfaceBuilderSupplier, private val roundedCornersFactory: LetterboxRoundedCornersDrawableFactory, private val letterboxConfiguration: LetterboxConfiguration, ) { fun create(conf: Configuration, parentLeash: SurfaceControl): RoundedCornersSurface = RoundedCornersSurface( ctx, conf, syncQueue, parentLeash, roundedCornersFactory, letterboxConfiguration, surfaceBuilderSupplier, ) }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt +1 −1 Original line number Original line Diff line number Diff line Loading @@ -43,7 +43,7 @@ abstract class LetterboxControllerRobotTest { val ANOTHER_TASK_ID = 10 val ANOTHER_TASK_ID = 10 @JvmStatic @JvmStatic private val TOKEN = mock<WindowContainerToken>() val TOKEN = mock<WindowContainerToken>() } } lateinit var letterboxController: LetterboxController lateinit var letterboxController: LetterboxController Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/roundedcorners/RoundedCornersLetterboxControllerTest.kt 0 → 100644 +206 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.roundedcorners import android.content.res.Configuration import android.graphics.Rect import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.compatui.letterbox.LetterboxController import com.android.wm.shell.compatui.letterbox.LetterboxControllerRobotTest import com.android.wm.shell.compatui.letterbox.LetterboxControllerRobotTest.Companion.ANOTHER_TASK_ID import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoRepository import com.android.wm.shell.compatui.letterbox.state.LetterboxTaskInfoState import java.util.function.Consumer import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify /** * Tests for [RoundedCornersLetterboxController]. * * Build/Install/Run: * atest WMShellUnitTests:RoundedCornersLetterboxControllerTest */ @RunWith(AndroidTestingRunner::class) @SmallTest class RoundedCornersLetterboxControllerTest : ShellTestCase() { @Test fun `Rounded corners are NOT created if configuration is missing from the repository`() { runTestScenario { r -> r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked(times = 0) } } @Test fun `Rounded corners are created if configuration is missing from the repository`() { runTestScenario { r -> r.configureRepository() r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked(times = 1) } } @Test fun `When creation is requested multiple times rounded corners are created once`() { runTestScenario { r -> r.configureRepository() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked(times = 1) } } @Test fun `A different surface is created for every key`() { runTestScenario { r -> r.configureRepository() r.configureRepository(taskId = ANOTHER_TASK_ID) r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest() r.sendCreateSurfaceRequest(taskId = ANOTHER_TASK_ID) r.sendCreateSurfaceRequest(taskId = ANOTHER_TASK_ID) r.checkSurfaceBuilderInvoked(times = 2) } } @Test fun `Created surface is removed once`() { runTestScenario { r -> r.configureRepository() r.sendCreateSurfaceRequest() r.checkSurfaceBuilderInvoked() r.sendDestroySurfaceRequest() r.sendDestroySurfaceRequest() r.sendDestroySurfaceRequest() r.checkRoundedCornersSurfaceReleased() } } @Test fun `Only existing surfaces receive visibility update`() { runTestScenario { r -> r.configureRepository() r.configureRepository(taskId = ANOTHER_TASK_ID) r.sendCreateSurfaceRequest() r.sendUpdateSurfaceVisibilityRequest(visible = true) r.sendUpdateSurfaceVisibilityRequest(visible = true, taskId = ANOTHER_TASK_ID) r.checkRoundedCornersVisibilityUpdated(times = 1, expectedVisibility = true) } } @Test fun `Only existing surfaces receive taskBounds update`() { runTestScenario { r -> r.configureRepository() r.sendUpdateSurfaceBoundsRequest( taskBounds = Rect(0, 0, 2000, 1000), activityBounds = Rect(500, 0, 1500, 1000) ) r.checkSurfacePositionUpdated(times = 0) r.checkSurfaceSizeUpdated(times = 0) r.resetTransitionTest() r.sendCreateSurfaceRequest() r.sendUpdateSurfaceBoundsRequest( taskBounds = Rect(0, 0, 2000, 1000), activityBounds = Rect(500, 0, 1500, 1000) ) } } /** * Runs a test scenario providing a Robot. */ fun runTestScenario(consumer: Consumer<RoundedCornersControllerRobotTest>) { consumer.accept(RoundedCornersControllerRobotTest().apply { initController() }) } class RoundedCornersControllerRobotTest : LetterboxControllerRobotTest() { private val letterboxRepository: LetterboxTaskInfoRepository private val roundedCornersSurfaceBuilder: RoundedCornersSurfaceBuilder private val roundedCornersSurface: RoundedCornersSurface private val executor: TestShellExecutor private val testConfiguration: Configuration init { testConfiguration = Configuration() executor = TestShellExecutor() letterboxRepository = LetterboxTaskInfoRepository() roundedCornersSurface = mock<RoundedCornersSurface>() roundedCornersSurfaceBuilder = mock<RoundedCornersSurfaceBuilder>() doReturn(roundedCornersSurface).`when`(roundedCornersSurfaceBuilder).create( any(), any() ) } override fun buildController(): LetterboxController = RoundedCornersLetterboxController( executor, roundedCornersSurfaceBuilder, letterboxRepository ) fun configureRepository(taskId: Int = TASK_ID) { letterboxRepository.insert( taskId, LetterboxTaskInfoState(TOKEN, parentLeash, configuration = testConfiguration) ) } fun checkSurfaceBuilderInvoked(times: Int = 1) { verify(roundedCornersSurfaceBuilder, times(times)).create( eq(testConfiguration), eq(parentLeash) ) } fun checkRoundedCornersVisibilityUpdated(times: Int = 1, expectedVisibility: Boolean) { verify(roundedCornersSurface, times(times)).setCornersVisibility( any(), eq(expectedVisibility), eq(true) ) } fun checkRoundedCornersSurfaceReleased(times: Int = 1) { verify(roundedCornersSurface, times(times)).release() } } }