diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt new file mode 100644 index 0000000000000..d01a0ee67f256 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 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.flicker.appcompat + +import android.content.Context +import android.system.helpers.CommandsHelper +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import com.android.wm.shell.flicker.BaseTest +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.LetterboxAppHelper +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.IFlickerTestData +import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd +import com.android.wm.shell.flicker.appWindowIsVisibleAtStart +import org.junit.Assume +import org.junit.Before +import org.junit.runners.Parameterized + +abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { + protected val context: Context = instrumentation.context + protected val letterboxApp = LetterboxAppHelper(instrumentation) + lateinit var cmdHelper: CommandsHelper + lateinit var letterboxStyle: HashMap + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + setStartRotation() + letterboxApp.launchViaIntent(wmHelper) + setEndRotation() + } + } + + @Before + fun before() { + cmdHelper = CommandsHelper.getInstance(instrumentation) + Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest()) + } + + private fun mapLetterboxStyle(): HashMap { + val res = cmdHelper.executeShellCommand("wm get-letterbox-style") + val lines = res.lines() + val map = HashMap() + for (line in lines) { + val keyValuePair = line.split(":") + if (keyValuePair.size == 2) { + val key = keyValuePair[0].trim() + map[key] = keyValuePair[1].trim() + } + } + return map + } + + private fun isIgnoreOrientationRequest(): Boolean { + val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request") + return res != null && res.contains("true") + } + + fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation) + + fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation) + + /** Checks that app entering letterboxed state have rounded corners */ + fun assertLetterboxAppAtStartHasRoundedCorners() { + assumeLetterboxRoundedCornersEnabled() + flicker.assertLayersStart { this.hasRoundedCorners(letterboxApp) } + } + + fun assertLetterboxAppAtEndHasRoundedCorners() { + assumeLetterboxRoundedCornersEnabled() + flicker.assertLayersEnd { this.hasRoundedCorners(letterboxApp) } + } + + /** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */ + private fun assumeLetterboxRoundedCornersEnabled() { + if (!::letterboxStyle.isInitialized) { + letterboxStyle = mapLetterboxStyle() + } + Assume.assumeTrue(letterboxStyle.getValue("Corner radius") != "0") + } + + fun assertLetterboxAppVisibleAtStartAndEnd() { + flicker.appWindowIsVisibleAtStart(letterboxApp) + flicker.appWindowIsVisibleAtEnd(letterboxApp) + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.rotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection { + return FlickerTestFactory.rotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt new file mode 100644 index 0000000000000..c57100e44c178 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 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.flicker.appcompat + +import android.platform.test.annotations.Postsubmit +import androidx.test.filters.RequiresDevice +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.common.datatypes.component.ComponentNameMatcher +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * Test launching app in size compat mode. + * + * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest` + * + * Actions: + * ``` + * Rotate non resizable portrait only app to opposite orientation to trigger size compat mode + * ``` + * Notes: + * ``` + * Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [BaseTest] + * ``` + */ + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) { + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + setStartRotation() + letterboxApp.launchViaIntent(wmHelper) + } + transitions { setEndRotation() } + teardown { letterboxApp.exit(wmHelper) } + } + + /** + * Windows maybe recreated when rotated. Checks that the focus does not change or if it does, + * focus returns to [letterboxApp] + */ + @Postsubmit + @Test + fun letterboxAppFocusedAtEnd() = flicker.assertEventLog { focusChanges(letterboxApp.`package`) } + + @Postsubmit + @Test + fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners() + + /** + * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't + * flicker, and disappears before the transition is complete + */ + @Postsubmit + @Test + fun rotationLayerAppearsAndVanishes() { + flicker.assertLayers { + this.isVisible(letterboxApp) + .then() + .isVisible(ComponentNameMatcher.ROTATION) + .then() + .isVisible(letterboxApp) + .isInvisible(ComponentNameMatcher.ROTATION) + } + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt new file mode 100644 index 0000000000000..f111a8d62d83e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 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.flicker.appcompat + +import android.platform.test.annotations.Postsubmit +import androidx.test.filters.RequiresDevice +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.helpers.WindowUtils +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * Test restarting app in size compat mode. + * + * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest` + * + * Actions: + * ``` + * Rotate app to opposite orientation to trigger size compat mode + * Press restart button and wait for letterboxed app to resize + * ``` + * Notes: + * ``` + * Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [BaseTest] + * ``` + */ + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) { + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + transitions { letterboxApp.clickRestart(wmHelper) } + teardown { letterboxApp.exit(wmHelper) } + } + + @Postsubmit + @Test + fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd() + + @Postsubmit + @Test + fun appLayerVisibilityChanges() { + flicker.assertLayers { + this.isVisible(letterboxApp) + .then() + .isInvisible(letterboxApp) + .then() + .isVisible(letterboxApp) + } + } + + @Postsubmit + @Test + fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtStartHasRoundedCorners() + + /** Checks that the visible region of [letterboxApp] is still within display bounds */ + @Postsubmit + @Test + fun appWindowRemainInsideVisibleBounds() { + val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) + flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) } + } +} \ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt new file mode 100644 index 0000000000000..1ccac13c280fb --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 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.server.wm.flicker.helpers + +import android.app.Instrumentation +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.testapp.ActivityOptions +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.traces.parsers.toFlickerComponent +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.helpers.SYSTEMUI_PACKAGE + +class LetterboxAppHelper +@JvmOverloads +constructor( + instr: Instrumentation, + launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL, + component: ComponentNameMatcher = + ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent() +) : StandardAppHelper(instr, launcherName, component) { + + fun clickRestart(wmHelper: WindowManagerStateHelper) { + val restartButton = uiDevice.wait(Until.findObject(By.res( + SYSTEMUI_PACKAGE, "size_compat_restart_button")), FIND_TIMEOUT) + restartButton?.run { restartButton.click() } ?: error("Restart button not found") + + // size compat mode restart confirmation dialog button + val restartDialogButton = uiDevice.wait(Until.findObject(By.res( + SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")), FIND_TIMEOUT) + restartDialogButton?.run { restartDialogButton.click() } + ?: error("Restart dialog button not found") + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 5361d73f93b55..1ec9ec9b0eda1 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -88,6 +88,18 @@ + + + + + +