diff options
62 files changed, 2671 insertions, 513 deletions
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java index 06e4c5002776..d605430bcf14 100644 --- a/core/java/android/util/imetracing/ImeTracingServerImpl.java +++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java @@ -41,9 +41,9 @@ import java.io.PrintWriter; */ class ImeTracingServerImpl extends ImeTracing { private static final String TRACE_DIRNAME = "/data/misc/wmtrace/"; - private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.pb"; - private static final String TRACE_FILENAME_IMS = "ime_trace_service.pb"; - private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.pb"; + private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.winscope"; + private static final String TRACE_FILENAME_IMS = "ime_trace_service.winscope"; + private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.winscope"; private static final int BUFFER_CAPACITY = 4096 * 1024; // Needed for winscope to auto-detect the dump type. Explained further in diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java index 10224a4b9db6..353c6c083d9d 100644 --- a/core/java/com/android/internal/protolog/ProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java @@ -28,7 +28,7 @@ import java.io.File; */ public class ProtoLogImpl extends BaseProtoLogImpl { private static final int BUFFER_CAPACITY = 1024 * 1024; - private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb"; + private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.winscope"; private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz"; private static ProtoLogImpl sServiceInstance = null; diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index d4b4e5daf7cb..1529f5bcd7e1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -17,11 +17,14 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation +import android.graphics.Rect import android.media.session.MediaController import android.media.session.MediaSessionManager import android.os.SystemClock import androidx.test.uiautomator.By import androidx.test.uiautomator.BySelector +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.FIND_TIMEOUT import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow @@ -85,6 +88,10 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( // Wait on WMHelper or simply wait for 3 seconds wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000) + // when entering pip, the dismiss button is visible at the start. to ensure the pip + // animation is complete, wait until the pip dismiss button is no longer visible. + // b/176822698: dismiss-only state will be removed in the future + uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT) } fun clickStartMediaSessionButton() { @@ -113,25 +120,25 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } } + private fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect { + val windowRegion = wmHelper.getWindowRegion(component) + require(!windowRegion.isEmpty) { + "Unable to find a PIP window in the current state" + } + return windowRegion.bounds + } + /** * Expands the pip window and dismisses it by clicking on the X button. - * - * Note, currently the View coordinates reported by the accessibility are relative to - * the window, so the correct coordinates need to be calculated - * - * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the - * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in - * Point(716, 66), instead of Point(970, 1403) - * - * See b/179337864 */ fun closePipWindow(wmHelper: WindowManagerStateHelper) { if (isTelevision) { uiDevice.closeTvPipWindow() } else { - expandPipWindow(wmHelper) + val windowRect = getWindowRect(wmHelper) + uiDevice.click(windowRect.centerX(), windowRect.centerY()) val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss")) - requireNotNull(exitPipObject) { "PIP window dismiss button not found" } + ?: error("PIP window dismiss button not found") val dismissButtonBounds = exitPipObject.visibleBounds uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY()) } @@ -142,32 +149,29 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } /** - * Click once on the PIP window to expand it + * Close the pip window by pressing the expand button */ - fun expandPipWindow(wmHelper: WindowManagerStateHelper) { - val windowRegion = wmHelper.getWindowRegion(component) - require(!windowRegion.isEmpty) { - "Unable to find a PIP window in the current state" - } - val windowRect = windowRegion.bounds + fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) { + val windowRect = getWindowRect(wmHelper) uiDevice.click(windowRect.centerX(), windowRect.centerY()) - // Ensure WindowManagerService wait until all animations have completed + // search and interact with the expand button + val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button") + uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT) + val expandPipObject = uiDevice.findObject(expandSelector) + ?: error("PIP window expand button not found") + val expandButtonBounds = expandPipObject.visibleBounds + uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY()) + wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() } wmHelper.waitForAppTransitionIdle() - mInstrumentation.uiAutomation.syncInputTransactions() } /** - * Double click on the PIP window to reopen to app + * Double click on the PIP window to expand it */ - fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) { - val windowRegion = wmHelper.getWindowRegion(component) - require(!windowRegion.isEmpty) { - "Unable to find a PIP window in the current state" - } - val windowRect = windowRegion.bounds + fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) { + val windowRect = getWindowRect(wmHelper) uiDevice.click(windowRect.centerX(), windowRect.centerY()) uiDevice.click(windowRect.centerX(), windowRect.centerY()) - wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() } wmHelper.waitForAppTransitionIdle() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt index af99fc4af1a0..ff34364261f2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.content.ComponentName -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest @@ -113,7 +112,7 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt index 7b4b71b41967..5fb6f1fa1076 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt @@ -104,7 +104,7 @@ class LegacySplitScreenToLauncher( @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt index ec0c73a58846..a7ac6a77d425 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt @@ -28,7 +28,6 @@ import com.android.server.wm.flicker.annotation.Group2 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.appPairsDividerBecomesVisible @@ -77,7 +76,7 @@ class OpenAppToLegacySplitScreen( @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt index d7f71a83ba7e..cd150518f21f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt @@ -129,7 +129,7 @@ class ResizeLegacySplitScreen( fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible() @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Test fun navBarLayerRotatesAndScales() = diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 0f0a4abe30ef..046972d246e6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -23,8 +23,10 @@ import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.LAUNCHER_COMPONENT import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.traces.parser.toLayerName import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -32,8 +34,21 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip launch. + * Test entering pip from an app by interacting with the app UI + * * To run this test: `atest WMShellFlickerTests:EnterPipTest` + * + * Actions: + * Launch an app in full screen + * Press an "enter pip" button to put [pipApp] in pip mode + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @@ -41,14 +56,19 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true, stringExtras = emptyMap()) { transitions { pipApp.clickEnterPipButton(wmHelper) - pipApp.expandPipWindow(wmHelper) } } + /** + * Checks [pipApp] window remains visible throughout the animation + */ @Presubmit @Test fun pipAppWindowAlwaysVisible() { @@ -57,6 +77,9 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } + /** + * Checks [pipApp] layer remains visible throughout the animation + */ @Presubmit @Test fun pipAppLayerAlwaysVisible() { @@ -65,19 +88,89 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @FlakyTest + /** + * Checks that the pip app window remains inside the display bounds throughout the whole + * animation + */ + @Presubmit @Test - fun pipWindowBecomesVisible() { + fun pipWindowRemainInsideVisibleBounds() { testSpec.assertWm { - invoke("pipWindowIsNotVisible") { - verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue() - }.then().invoke("pipWindowIsVisible") { - verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue() + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks that the pip app layer remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + fun pipLayerRemainInsideVisibleBounds() { + testSpec.assertLayers { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks that the visible region of [pipApp] always reduces during the animation + */ + @Presubmit + @Test + fun pipLayerReduces() { + val layerName = pipApp.component.toLayerName() + testSpec.assertLayers { + val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.coversAtMost(previous.visibleRegion.region) } } } + /** + * Checks that [pipApp] window becomes pinned + */ + @Presubmit + @Test + fun pipWindowBecomesPinned() { + testSpec.assertWm { + invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp.component) } + .then() + .invoke("pipWindowIsPinned") { it.isPinned(pipApp.component) } + } + } + + /** + * Checks [LAUNCHER_COMPONENT] layer remains visible throughout the animation + */ + @Presubmit + @Test + fun launcherLayerBecomesVisible() { + testSpec.assertLayers { + isInvisible(LAUNCHER_COMPONENT) + .then() + .isVisible(LAUNCHER_COMPONENT) + } + } + + /** + * Checks the focus doesn't change during the animation + */ + @FlakyTest + @Test + fun focusDoesNotChange() { + testSpec.assertEventLog { + this.focusDoesNotChange() + } + } + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index 67ad322f19f6..097ccb8cd734 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -25,7 +25,11 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT @@ -38,8 +42,22 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip with orientation changes. - * To run this test: `atest WMShellFlickerTests:PipOrientationTest` + * Test entering pip while changing orientation (from app in landscape to pip window in portrait) + * + * To run this test: `atest EnterPipToOtherOrientationTest:EnterPipToOtherOrientationTest` + * + * Actions: + * Launch [testApp] on a fixed portrait orientation + * Launch [pipApp] on a fixed landscape orientation + * Broadcast action [ACTION_ENTER_PIP] to enter pip mode + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @@ -53,6 +71,9 @@ class EnterPipToOtherOrientationTest( private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setupAndTeardown(this, configuration) @@ -79,21 +100,41 @@ class EnterPipToOtherOrientationTest( broadcastActionTrigger.doAction(ACTION_ENTER_PIP) wmHelper.waitFor { it.wmState.hasPipWindow() } wmHelper.waitForAppTransitionIdle() + // during rotation the status bar becomes invisible and reappears at the end + wmHelper.waitForNavBarStatusBarVisible() } } + /** + * Checks that the [WindowManagerStateHelper.NAV_BAR_COMPONENT] has the correct position at + * the start and end of the transition + */ @FlakyTest @Test - override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + override fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0) - @FlakyTest + /** + * Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at + * the start and end of the transition + */ + @Presubmit @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + override fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_90, Surface.ROTATION_0) - @FlakyTest + /** + * Checks that all parts of the screen are covered at the start and end of the transition + * + * TODO b/197726599 Prevents all states from being checked + */ + @Presubmit @Test - override fun entireScreenCovered() = super.entireScreenCovered() + override fun entireScreenCovered() = testSpec.entireScreenCovered(allStates = false) + /** + * Checks [pipApp] window remains visible and on top throughout the transition + */ @Presubmit @Test fun pipAppWindowIsAlwaysOnTop() { @@ -102,40 +143,84 @@ class EnterPipToOtherOrientationTest( } } + /** + * Checks that [testApp] window is not visible at the start + */ @Presubmit @Test - fun pipAppHidesTestApp() { + fun testAppWindowInvisibleOnStart() { testSpec.assertWmStart { isInvisible(testApp.component) } } + /** + * Checks that [testApp] window is visible at the end + */ @Presubmit @Test - fun testAppWindowIsVisible() { + fun testAppWindowVisibleOnEnd() { testSpec.assertWmEnd { isVisible(testApp.component) } } + /** + * Checks that [testApp] layer is not visible at the start + */ @Presubmit @Test - fun pipAppLayerHidesTestApp() { + fun testAppLayerInvisibleOnStart() { testSpec.assertLayersStart { - visibleRegion(pipApp.component).coversExactly(startingBounds) isInvisible(testApp.component) } } + /** + * Checks that [testApp] layer is visible at the end + */ + @Presubmit + @Test + fun testAppLayerVisibleOnEnd() { + testSpec.assertLayersEnd { + isVisible(testApp.component) + } + } + + /** + * Checks that the visible region of [pipApp] covers the full display area at the start of + * the transition + */ + @Presubmit + @Test + fun pipAppLayerCoversFullScreenOnStart() { + testSpec.assertLayersStart { + visibleRegion(pipApp.component).coversExactly(startingBounds) + } + } + + /** + * Checks that the visible region of [testApp] plus the visible region of [pipApp] + * cover the full display area at the end of the transition + */ @Presubmit @Test - fun testAppLayerCoversFullScreen() { + fun testAppPlusPipLayerCoversFullScreenOnEnd() { testSpec.assertLayersEnd { - visibleRegion(testApp.component).coversExactly(endingBounds) + val pipRegion = visibleRegion(pipApp.component).region + visibleRegion(testApp.component) + .plus(pipRegion) + .coversExactly(endingBounds) } } companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt new file mode 100644 index 000000000000..faaaecb8da46 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 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.pip + +import android.platform.test.annotations.Presubmit +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.traces.parser.toLayerName +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import org.junit.Test + +/** + * Base class for pip expand tests + */ +abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + protected val testApp = FixedAppHelper(instrumentation) + + /** + * Checks that the pip app window remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + open fun pipAppWindowRemainInsideVisibleBounds() { + testSpec.assertWm { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks that the pip app layer remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + open fun pipAppLayerRemainInsideVisibleBounds() { + testSpec.assertLayers { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks both app windows are visible at the start of the transition (with [pipApp] on top). + * Then, during the transition, [testApp] becomes invisible and [pipApp] remains visible + */ + @Presubmit + @Test + open fun showBothAppWindowsThenHidePip() { + testSpec.assertWm { + // when the activity is STOPPING, sometimes it becomes invisible in an entry before + // the window, sometimes in the same entry. This occurs because we log 1x per frame + // thus we ignore activity here + isAppWindowVisible(testApp.component, ignoreActivity = true) + .isAppWindowOnTop(pipApp.component) + .then() + .isAppWindowInvisible(testApp.component) + .isAppWindowVisible(pipApp.component) + } + } + + /** + * Checks both app layers are visible at the start of the transition. Then, during the + * transition, [testApp] becomes invisible and [pipApp] remains visible + */ + @Presubmit + @Test + open fun showBothAppLayersThenHidePip() { + testSpec.assertLayers { + isVisible(testApp.component) + .isVisible(pipApp.component) + .then() + .isInvisible(testApp.component) + .isVisible(pipApp.component) + } + } + + /** + * Checks that the visible region of [testApp] plus the visible region of [pipApp] + * cover the full display area at the start of the transition + */ + @Presubmit + @Test + open fun testPlusPipAppsCoverFullScreenAtStart() { + testSpec.assertLayersStart { + val pipRegion = visibleRegion(pipApp.component).region + visibleRegion(testApp.component) + .plus(pipRegion) + .coversExactly(displayBounds) + } + } + + /** + * Checks that the visible region of [pipApp] covers the full display area at the end of + * the transition + */ + @Presubmit + @Test + open fun pipAppCoversFullScreenAtEnd() { + testSpec.assertLayersEnd { + visibleRegion(pipApp.component).coversExactly(displayBounds) + } + } + + /** + * Checks that the visible region of [pipApp] always expands during the animation + */ + @Presubmit + @Test + open fun pipLayerExpands() { + val layerName = pipApp.component.toLayerName() + testSpec.assertLayers { + val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.coversAtLeast(previous.visibleRegion.region) + } + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt index 28b1028d41ca..3414031d3532 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt @@ -20,15 +20,16 @@ import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.LAUNCHER_COMPONENT import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation import org.junit.Test -import org.junit.runners.Parameterized -abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { +/** + * Base class for exiting pip (closing pip window) without returning to the app + */ +abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { configuration -> setup { @@ -43,16 +44,28 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio } } + /** + * Checks that [pipApp] window is pinned and visible at the start and then becomes + * unpinned and invisible at the same moment, and remains unpinned and invisible + * until the end of the transition + */ @Presubmit @Test open fun pipWindowBecomesInvisible() { testSpec.assertWm { - this.invoke("hasPipWindow") { it.isPinned(pipApp.component) } - .then() - .isAppWindowInvisible(pipApp.component) + this.invoke("hasPipWindow") { + it.isPinned(pipApp.component).isVisible(pipApp.component) + }.then().invoke("!hasPipWindow") { + it.isNotPinned(pipApp.component).isInvisible(pipApp.component) + } } } + /** + * Checks that [pipApp] and [LAUNCHER_COMPONENT] layers are visible at the start + * of the transition. Then [pipApp] layer becomes invisible, and remains invisible + * until the end of the transition + */ @Presubmit @Test open fun pipLayerBecomesInvisible() { @@ -65,6 +78,10 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio } } + /** + * Checks that the focus changes between the [pipApp] window and the launcher when + * closing the pip window + */ @FlakyTest(bugId = 151179149) @Test open fun focusChanges() { @@ -72,14 +89,4 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio this.focusChanges(pipApp.launcherName, "NexusLauncherActivity") } } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) - } - } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt new file mode 100644 index 000000000000..fa100b5b25ab --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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.pip + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group3 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.traces.parser.toWindowName +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test expanding a pip window back to full screen via the expand button + * + * To run this test: `atest WMShellFlickerTests:ExitPipViaExpandButtonClickTest` + * + * Actions: + * Launch an app in pip mode [pipApp], + * Launch another full screen mode [testApp] + * Expand [pipApp] app to full screen by clicking on the pip window and + * then on the expand button + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group3 +class ExitPipViaExpandButtonClickTest( + testSpec: FlickerTestParameter +) : ExitPipToAppTransition(testSpec) { + + /** + * Defines the transition used to run the test + */ + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = true) { + setup { + eachRun { + // launch an app behind the pip one + testApp.launchViaIntent(wmHelper) + } + } + transitions { + // This will bring PipApp to fullscreen + pipApp.expandPipWindowToApp(wmHelper) + // Wait until the other app is no longer visible + wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName()) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index 39e89fbd9b71..617ef221ee9a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -16,8 +16,6 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Postsubmit -import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -25,91 +23,63 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.server.wm.traces.parser.toWindowName import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip launch and exit. - * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` + * Test expanding a pip window back to full screen via an intent + * + * To run this test: `atest WMShellFlickerTests:ExitPipViaIntentTest` + * + * Actions: + * Launch an app in pip mode [pipApp], + * Launch another full screen mode [testApp] + * Expand [pipApp] app to full screen via an intent + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited from [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 -class EnterExitPipTest( - testSpec: FlickerTestParameter -) : PipTransition(testSpec) { - private val testApp = FixedAppHelper(instrumentation) +class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) { + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { setup { eachRun { + // launch an app behind the pip one testApp.launchViaIntent(wmHelper) } } transitions { // This will bring PipApp to fullscreen pipApp.launchViaIntent(wmHelper) + // Wait until the other app is no longer visible + wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName()) } } - @Presubmit - @Test - fun pipAppRemainInsideVisibleBounds() { - testSpec.assertWm { - coversAtMost(displayBounds, pipApp.component) - } - } - - @Postsubmit - @Test - fun showBothAppWindowsThenHidePip() { - testSpec.assertWm { - // when the activity is STOPPING, sometimes it becomes invisible in an entry before - // the window, sometimes in the same entry. This occurs because we log 1x per frame - // thus we ignore activity here - isAppWindowVisible(testApp.component, ignoreActivity = true) - .isAppWindowOnTop(pipApp.component) - .then() - .isAppWindowInvisible(testApp.component) - } - } - - @Presubmit - @Test - fun showBothAppLayersThenHidePip() { - testSpec.assertLayers { - isVisible(testApp.component) - .isVisible(pipApp.component) - .then() - .isInvisible(testApp.component) - } - } - - @Presubmit - @Test - fun testAppCoversFullScreenWithPipOnDisplay() { - testSpec.assertLayersStart { - visibleRegion(testApp.component).coversExactly(displayBounds) - visibleRegion(pipApp.component).coversAtMost(displayBounds) - } - } - - @Presubmit - @Test - fun pipAppCoversFullScreen() { - testSpec.assertLayersEnd { - visibleRegion(pipApp.component).coversExactly(displayBounds) - } - } - companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt index 1c5d77f68f43..e3d099f6fdb5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt @@ -16,10 +16,12 @@ package com.android.wm.shell.flicker.pip -import androidx.test.filters.FlakyTest +import android.platform.test.annotations.Postsubmit +import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder import org.junit.FixMethodOrder @@ -29,15 +31,29 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton` + * Test closing a pip window via the dismiss button + * + * To run this test: `atest WMShellFlickerTests:ExitPipWithDismissButtonTest` + * + * Actions: + * Launch an app in pip mode [pipApp], + * Click on the pip window + * Click on dismiss button and wait window disappear + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 -class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) { +class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) { override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) @@ -46,11 +62,27 @@ class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTr } } - @FlakyTest + @Postsubmit @Test override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible() - @FlakyTest + @Postsubmit @Test override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible() + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) + } + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt index 356ec94b97e3..2cdfc2bf0654 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt @@ -22,6 +22,7 @@ import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.startRotation @@ -33,24 +34,40 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe` + * Test closing a pip window by swiping it to the bottom-center of the screen + * + * To run this test: `atest WMShellFlickerTests:ExitPipWithSwipeDownTest` + * + * Actions: + * Launch an app in pip mode [pipApp], + * Swipe the pip window to the bottom-center of the screen and wait it disappear + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 -class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) { +class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) { override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit - get() = { - super.transition(this, it) + get() = { args -> + super.transition(this, args) transitions { val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds val pipCenterX = pipRegion.centerX() val pipCenterY = pipRegion.centerY() val displayCenterX = device.displayWidth / 2 - device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5) + device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10) + wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() } + wmHelper.waitForWindowSurfaceDisappeared(pipApp.component) + wmHelper.waitForAppTransitionIdle() } } @@ -90,4 +107,20 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition @Presubmit @Test override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 20) + } + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt new file mode 100644 index 000000000000..89b2c400e80a --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 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.pip + +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.view.Surface +import androidx.test.filters.FlakyTest +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.LAUNCHER_COMPONENT +import com.android.server.wm.flicker.annotation.Group3 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.traces.parser.toLayerName +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test expanding a pip window by double clicking it + * + * To run this test: `atest WMShellFlickerTests:ExpandPipOnDoubleClickTest` + * + * Actions: + * Launch an app in pip mode [pipApp], + * Expand [pipApp] app to its maximum pip size by double clicking on it + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group3 +class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = true) { + transitions { + pipApp.doubleClickPipWindow(wmHelper) + } + } + + /** + * Checks that the pip app window remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + fun pipWindowRemainInsideVisibleBounds() { + testSpec.assertWm { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks that the pip app layer remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + fun pipLayerRemainInsideVisibleBounds() { + testSpec.assertLayers { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks [pipApp] window remains visible throughout the animation + */ + @Postsubmit + @Test + fun pipWindowIsAlwaysVisible() { + testSpec.assertWm { + isAppWindowVisible(pipApp.component) + } + } + + /** + * Checks [pipApp] layer remains visible throughout the animation + */ + @Presubmit + @Test + fun pipLayerIsAlwaysVisible() { + testSpec.assertLayers { + isVisible(pipApp.component) + } + } + + /** + * Checks that the visible region of [pipApp] always expands during the animation + */ + @Presubmit + @Test + fun pipLayerExpands() { + val layerName = pipApp.component.toLayerName() + testSpec.assertLayers { + val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.coversAtLeast(previous.visibleRegion.region) + } + } + } + + /** + * Checks [pipApp] window remains pinned throughout the animation + */ + @Presubmit + @Test + fun windowIsAlwaysPinned() { + testSpec.assertWm { + this.invoke("hasPipWindow") { it.isPinned(pipApp.component) } + } + } + + /** + * Checks [pipApp] layer remains visible throughout the animation + */ + @Presubmit + @Test + fun launcherIsAlwaysVisible() { + testSpec.assertLayers { + isVisible(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the focus doesn't change between windows during the transition + */ + @FlakyTest + @Test + fun focusDoesNotChange() { + testSpec.assertEventLog { + this.focusDoesNotChange() + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt new file mode 100644 index 000000000000..0ab857d755ee --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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.pip + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group3 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.traces.RegionSubject +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip movement with Launcher shelf height change (decrease). + * + * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest` + * + * Actions: + * Launch [pipApp] in pip mode + * Launch [testApp] + * Press home + * Check if pip window moves down (visually) + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group3 +class MovePipDownShelfHeightChangeTest( + testSpec: FlickerTestParameter +) : MovePipShelfHeightTransition(testSpec) { + /** + * Defines the transition used to run the test + */ + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = false) { + teardown { + eachRun { + testApp.launchViaIntent(wmHelper) + } + test { + testApp.exit(wmHelper) + } + } + transitions { + taplInstrumentation.pressHome() + } + } + + override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) { + current.isHigherOrEqual(previous.region) + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt new file mode 100644 index 000000000000..ed04fc947435 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 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.pip + +import android.platform.test.annotations.Presubmit +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.traces.RegionSubject +import com.android.server.wm.traces.parser.toLayerName +import com.android.server.wm.traces.parser.toWindowName +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import org.junit.Test + +/** + * Base class for pip tests with Launcher shelf height change + */ +abstract class MovePipShelfHeightTransition( + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + protected val taplInstrumentation = LauncherInstrumentation() + protected val testApp = FixedAppHelper(instrumentation) + + /** + * Checks if the window movement direction is valid + */ + protected abstract fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) + + /** + * Checks [pipApp] window remains visible throughout the animation + */ + @Presubmit + @Test + open fun pipWindowIsAlwaysVisible() { + testSpec.assertWm { + isAppWindowVisible(pipApp.component) + } + } + + /** + * Checks [pipApp] layer remains visible throughout the animation + */ + @Presubmit + @Test + open fun pipLayerIsAlwaysVisible() { + testSpec.assertLayers { + isVisible(pipApp.component) + } + } + + /** + * Checks that the pip app window remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + open fun pipWindowRemainInsideVisibleBounds() { + testSpec.assertWm { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks that the pip app layer remains inside the display bounds throughout the whole + * animation + */ + @Presubmit + @Test + open fun pipLayerRemainInsideVisibleBounds() { + testSpec.assertLayers { + coversAtMost(displayBounds, pipApp.component) + } + } + + /** + * Checks that the visible region of [pipApp] always moves in the correct direction + * during the animation. + */ + @Presubmit + @Test + open fun pipWindowMoves() { + val windowName = pipApp.component.toWindowName() + testSpec.assertWm { + val pipWindowList = this.windowStates { it.name.contains(windowName) && it.isVisible } + pipWindowList.zipWithNext { previous, current -> + assertRegionMovement(previous.frame, current.frame) + } + } + } + + /** + * Checks that the visible region of [pipApp] always moves up during the animation + */ + @Presubmit + @Test + open fun pipLayerMoves() { + val layerName = pipApp.component.toLayerName() + testSpec.assertLayers { + val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } + pipLayerList.zipWithNext { previous, current -> + assertRegionMovement(previous.visibleRegion, current.visibleRegion) + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt index 914bc8b4d1c9..e507edfda48c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt @@ -16,36 +16,49 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice -import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.google.common.truth.Truth +import com.android.server.wm.flicker.traces.RegionSubject import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip movement with Launcher shelf height change. - * To run this test: `atest WMShellFlickerTests:PipShelfHeightTest` + * Test Pip movement with Launcher shelf height change (increase). + * + * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest` + * + * Actions: + * Launch [pipApp] in pip mode + * Press home + * Launch [testApp] + * Check if pip window moves up (visually) + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 -class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - private val taplInstrumentation = LauncherInstrumentation() - private val testApp = FixedAppHelper(instrumentation) - +class MovePipUpShelfHeightChangeTest( + testSpec: FlickerTestParameter +) : MovePipShelfHeightTransition(testSpec) { + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = false) { teardown { @@ -61,33 +74,17 @@ class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpe } } - @Presubmit - @Test - fun pipAlwaysVisible() = testSpec.assertWm { this.isAppWindowVisible(pipApp.component) } - - @Presubmit - @Test - fun pipLayerInsideDisplay() { - testSpec.assertLayersStart { - visibleRegion(pipApp.component).coversAtMost(displayBounds) - } - } - - @Presubmit - @Test - fun pipWindowMovesUp() = testSpec.assertWmEnd { - val initialState = this.trace?.first()?.wmState - ?: error("Trace should not be empty") - val startPos = initialState.pinnedWindows.first().frame - val currPos = this.wmState.pinnedWindows.first().frame - val subject = Truth.assertWithMessage("Pip should have moved up") - subject.that(currPos.top).isGreaterThan(startPos.top) - subject.that(currPos.bottom).isGreaterThan(startPos.bottom) - subject.that(currPos.left).isEqualTo(startPos.left) - subject.that(currPos.right).isEqualTo(startPos.right) + override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) { + current.isLowerOrEqual(previous.region) } companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 45cb152e5b0d..b1054600d702 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -32,8 +32,6 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.traces.common.Region -import com.android.server.wm.traces.parser.minus import com.android.wm.shell.flicker.helpers.FixedAppHelper import org.junit.FixMethodOrder import org.junit.Test @@ -43,7 +41,22 @@ import org.junit.runners.Parameterized /** * Test Pip Stack in bounds after rotations. + * * To run this test: `atest WMShellFlickerTests:PipRotationTest` + * + * Actions: + * Launch a [pipApp] in pip mode + * Launch another app [fixedApp] (appears below pip) + * Rotate the screen from [testSpec.config.startRotation] to [testSpec.config.endRotation] + * (usually, 0->90 and 90->0) + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited from [PipTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @@ -52,8 +65,8 @@ import org.junit.runners.Parameterized @Group3 class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { private val fixedApp = FixedAppHelper(instrumentation) - private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation) + private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.config.endRotation) override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = false) { configuration -> @@ -68,55 +81,108 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) transitions { setRotation(configuration.endRotation) } - teardown { - eachRun { - setRotation(Surface.ROTATION_0) - } - } } + /** + * Checks that all parts of the screen are covered at the start and end of the transition + */ @Presubmit @Test - override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation, - testSpec.config.endRotation, allStates = false) + override fun entireScreenCovered() = testSpec.entireScreenCovered() + /** + * Checks the position of the navigation bar at the start and end of the transition + */ @FlakyTest @Test override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, testSpec.config.endRotation) + /** + * Checks the position of the status bar at the start and end of the transition + */ @Presubmit @Test override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, testSpec.config.endRotation) + /** + * Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition + */ @Presubmit @Test fun appLayerRotates_StartingBounds() { testSpec.assertLayersStart { - val pipRegion = visibleRegion(pipApp.component).region - val expectedWithoutPip = Region(startingBounds.bounds.left, startingBounds.bounds.top, - startingBounds.bounds.right, startingBounds.bounds.bottom).minus(pipRegion) - visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip) - visibleRegion(pipApp.component).coversAtMost(startingBounds) + visibleRegion(fixedApp.component).coversExactly(screenBoundsStart) } } + /** + * Checks that [fixedApp] layer is within [screenBoundsEnd] at the end of the transition + */ @Presubmit @Test fun appLayerRotates_EndingBounds() { testSpec.assertLayersEnd { - val pipRegion = visibleRegion(pipApp.component).region - val expectedWithoutPip = Region(endingBounds.bounds.left, endingBounds.bounds.top, - endingBounds.bounds.right, endingBounds.bounds.bottom).minus(pipRegion) - visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip) - visibleRegion(pipApp.component).coversAtMost(endingBounds) + visibleRegion(fixedApp.component).coversExactly(screenBoundsEnd) + } + } + + /** + * Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition + */ + @Presubmit + @Test + fun pipLayerRotates_StartingBounds() { + testSpec.assertLayersStart { + visibleRegion(pipApp.component).coversAtMost(screenBoundsStart) + } + } + + /** + * Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition + */ + @Presubmit + @Test + fun pipLayerRotates_EndingBounds() { + testSpec.assertLayersEnd { + visibleRegion(pipApp.component).coversAtMost(screenBoundsEnd) + } + } + + /** + * Ensure that the [pipApp] window does not obscure the [fixedApp] at the start of the + * transition + */ + @Presubmit + @Test + fun pipIsAboveFixedAppWindow_Start() { + testSpec.assertWmStart { + isAboveWindow(pipApp.component, fixedApp.component) + } + } + + /** + * Ensure that the [pipApp] window does not obscure the [fixedApp] at the end of the + * transition + */ + @Presubmit + @Test + fun pipIsAboveFixedAppWindow_End() { + testSpec.assertWmEnd { + isAboveWindow(pipApp.component, fixedApp.component) } } companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt deleted file mode 100644 index 5abcf39f3fbd..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2020 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.pip - -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.LAUNCHER_COMPONENT -import com.android.server.wm.flicker.annotation.Group3 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.startRotation -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipToAppTest` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group3 -class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit - get() = buildTransition(eachRun = true) { configuration -> - setup { - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - pipApp.expandPipWindowToApp(wmHelper) - } - } - - @FlakyTest - @Test - fun appReplacesPipWindow() { - testSpec.assertWm { - this.invoke("hasPipWindow") { it.isPinned(pipApp.component) } - .isAppWindowOnTop(pipApp.component) - .then() - .invoke("hasNotPipWindow") { it.isNotPinned(pipApp.component) } - .isAppWindowOnTop(pipApp.component) - } - } - - @FlakyTest - @Test - fun appReplacesPipLayer() { - testSpec.assertLayers { - this.isVisible(pipApp.component) - .isVisible(LAUNCHER_COMPONENT) - .then() - .isVisible(pipApp.component) - .isInvisible(LAUNCHER_COMPONENT) - } - } - - @FlakyTest - @Test - fun testAppCoversFullScreen() { - testSpec.assertLayersStart { - visibleRegion(pipApp.component).coversExactly(displayBounds) - } - } - - @FlakyTest(bugId = 151179149) - @Test - fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges("NexusLauncherActivity", - pipApp.launcherName, "NexusLauncherActivity") - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index ca80d1837ea8..ce89fb6af67d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -187,6 +187,5 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { @Presubmit @Test - open fun entireScreenCovered() = - testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0) + open fun entireScreenCovered() = testSpec.entireScreenCovered() }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index e7b61970cbeb..02a3eb1e070a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -144,9 +144,7 @@ class SetRequestedOrientationWhilePinnedTest( @FlakyTest @Test - override fun entireScreenCovered() { - super.entireScreenCovered() - } + override fun entireScreenCovered() = super.entireScreenCovered() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/packages/SystemUI/res/drawable/ic_media_home_devices.xml b/packages/SystemUI/res/drawable/ic_media_home_devices.xml new file mode 100644 index 000000000000..886c64d9bf02 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_media_home_devices.xml @@ -0,0 +1,16 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M20,4H4c-1.1,0 -2,0.9 -2,2v11c0,1.1 0.9,2 2,2h4v2h3v-4H4V6h16v1h2V6c0,-1.1 -0.9,-2 -2,-2z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M17.5,16.5m-2.33,0a2.33,2.33 0,1 1,4.66 0a2.33,2.33 0,1 1,-4.66 0"/> + <path + android:fillColor="@android:color/white" + android:pathData="M21,8h-7c-0.55,0 -1,0.45 -1,1v11c0,0.55 0.45,1 1,1h7c0.55,0 1,-0.45 1,-1L22,9c0,-0.55 -0.45,-1 -1,-1zM17.5,9c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5 -1.5,-0.67 -1.5,-1.5 0.67,-1.5 1.5,-1.5zM17.5,20c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/> +</vector> diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index 075473aef9ac..566cd25e86a5 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -163,18 +163,6 @@ </LinearLayout> </LinearLayout> - <ImageView - android:id="@+id/media_seamless_fallback" - android:layout_width="@dimen/qs_seamless_fallback_icon_size" - android:layout_height="@dimen/qs_seamless_fallback_icon_size" - android:layout_marginTop="@dimen/qs_media_padding" - android:layout_marginBottom="@dimen/qs_media_padding" - android:layout_marginStart="@dimen/qs_center_guideline_padding" - android:layout_marginEnd="@dimen/qs_seamless_fallback_margin" - android:tint="?android:attr/textColor" - android:src="@drawable/ic_cast_connected" - android:forceHasOverlappingRendering="false" /> - <!-- Seek Bar --> <!-- As per Material Design on Biderectionality, this is forced to LTR in code --> <SeekBar diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 784814d5ca38..26ee5ea7f914 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1306,8 +1306,6 @@ <dimen name="qs_media_action_margin">12dp</dimen> <dimen name="qs_seamless_height">24dp</dimen> <dimen name="qs_seamless_icon_size">12dp</dimen> - <dimen name="qs_seamless_fallback_icon_size">@dimen/qs_seamless_icon_size</dimen> - <dimen name="qs_seamless_fallback_margin">20dp</dimen> <dimen name="qs_footer_horizontal_margin">22dp</dimen> <dimen name="qs_media_disabled_seekbar_height">1dp</dimen> <dimen name="qs_media_enabled_seekbar_height">2dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 59f1b9f562fe..03ba28a3c7f5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1008,7 +1008,7 @@ <string name="sensor_privacy_start_use_mic_camera_dialog_content">This unblocks access for all apps and services allowed to use your camera or microphone.</string> <!-- Default name for the media device shown in the output switcher when the name is not available [CHAR LIMIT=30] --> - <string name="media_seamless_remote_device">Device</string> + <string name="media_seamless_other_device">Other device</string> <!-- QuickStep: Accessibility to toggle overview [CHAR LIMIT=40] --> <string name="quick_step_accessibility_toggle_overview">Toggle Overview</string> diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml index d6c6a60d56b8..c3510b61e68a 100644 --- a/packages/SystemUI/res/xml/media_collapsed.xml +++ b/packages/SystemUI/res/xml/media_collapsed.xml @@ -44,23 +44,6 @@ /> <Constraint - android:id="@+id/media_seamless_fallback" - android:layout_width="@dimen/qs_seamless_fallback_icon_size" - android:layout_height="@dimen/qs_seamless_fallback_icon_size" - android:layout_marginTop="@dimen/qs_media_padding" - android:layout_marginBottom="@dimen/qs_media_padding" - android:layout_marginStart="@dimen/qs_center_guideline_padding" - android:layout_marginEnd="@dimen/qs_seamless_fallback_margin" - android:alpha="0.5" - android:visibility="gone" - app:layout_constraintHorizontal_bias="1" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline" - app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" - app:layout_constraintEnd_toEndOf="parent" - /> - - <Constraint android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size_small" android:layout_height="@dimen/qs_media_album_size_small" diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml index 0e284e6ad631..6b83aae38e15 100644 --- a/packages/SystemUI/res/xml/media_expanded.xml +++ b/packages/SystemUI/res/xml/media_expanded.xml @@ -45,22 +45,6 @@ android:layout_marginBottom="4dp" /> <Constraint - android:id="@+id/media_seamless_fallback" - android:layout_width="@dimen/qs_seamless_fallback_icon_size" - android:layout_height="@dimen/qs_seamless_fallback_icon_size" - android:layout_marginTop="@dimen/qs_media_padding" - android:layout_marginBottom="16dp" - android:layout_marginStart="@dimen/qs_center_guideline_padding" - android:layout_marginEnd="@dimen/qs_seamless_fallback_margin" - android:alpha="0.5" - android:visibility="gone" - app:layout_constraintHorizontal_bias="1" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" - app:layout_constraintEnd_toEndOf="parent" - /> - - <Constraint android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" android:layout_height="@dimen/qs_media_album_size" diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index afe9e5a6ee95..c125612380d3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -370,27 +370,16 @@ public class MediaControlPanel { final MediaDeviceData device = data.getDevice(); final int seamlessId = mPlayerViewHolder.getSeamless().getId(); - final int seamlessFallbackId = mPlayerViewHolder.getSeamlessFallback().getId(); - final boolean showFallback = device != null && !device.getEnabled(); - final int seamlessFallbackVisibility = showFallback ? View.VISIBLE : View.GONE; - mPlayerViewHolder.getSeamlessFallback().setVisibility(seamlessFallbackVisibility); - expandedSet.setVisibility(seamlessFallbackId, seamlessFallbackVisibility); - collapsedSet.setVisibility(seamlessFallbackId, seamlessFallbackVisibility); - final int seamlessVisibility = showFallback ? View.GONE : View.VISIBLE; - mPlayerViewHolder.getSeamless().setVisibility(seamlessVisibility); - expandedSet.setVisibility(seamlessId, seamlessVisibility); - collapsedSet.setVisibility(seamlessId, seamlessVisibility); - final float seamlessAlpha = data.getResumption() ? DISABLED_ALPHA : 1.0f; + // Disable clicking on output switcher for invalid devices and resumption controls + final boolean seamlessDisabled = (device != null && !device.getEnabled()) + || data.getResumption(); + final float seamlessAlpha = seamlessDisabled ? DISABLED_ALPHA : 1.0f; expandedSet.setAlpha(seamlessId, seamlessAlpha); collapsedSet.setAlpha(seamlessId, seamlessAlpha); - // Disable clicking on output switcher for resumption controls. - mPlayerViewHolder.getSeamless().setEnabled(!data.getResumption()); + mPlayerViewHolder.getSeamless().setEnabled(!seamlessDisabled); String deviceString = null; - if (showFallback) { - iconView.setImageDrawable(null); - } else if (device != null) { + if (device != null && device.getEnabled()) { Drawable icon = device.getIcon(); - iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { AdaptiveIcon aIcon = (AdaptiveIcon) icon; aIcon.setBackgroundColor(mBackgroundColor); @@ -401,10 +390,9 @@ public class MediaControlPanel { deviceString = device.getName(); } else { // Reset to default - Log.w(TAG, "device is null. Not binding output chip."); - iconView.setVisibility(View.GONE); - deviceString = mContext.getString( - com.android.internal.R.string.ext_media_seamless_action); + Log.w(TAG, "Device is null or not enabled: " + device + ", not binding output chip."); + iconView.setImageResource(R.drawable.ic_media_home_devices); + deviceString = mContext.getString(R.string.media_seamless_other_device); } deviceName.setText(deviceString); seamlessView.setContentDescription(deviceString); diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt index 791f59d3ae13..35603b6ef6cc 100644 --- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt @@ -43,7 +43,6 @@ class PlayerViewHolder private constructor(itemView: View) { val seamless = itemView.requireViewById<ViewGroup>(R.id.media_seamless) val seamlessIcon = itemView.requireViewById<ImageView>(R.id.media_seamless_image) val seamlessText = itemView.requireViewById<TextView>(R.id.media_seamless_text) - val seamlessFallback = itemView.requireViewById<ImageView>(R.id.media_seamless_fallback) // Seek bar val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar) @@ -124,7 +123,6 @@ class PlayerViewHolder private constructor(itemView: View) { R.id.header_title, R.id.header_artist, R.id.media_seamless, - R.id.media_seamless_fallback, R.id.notification_media_progress_time, R.id.media_progress_bar, R.id.action0, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index b129fdd0d844..42629f545559 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -37,6 +37,7 @@ import android.widget.TextView import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.media.dialog.MediaOutputDialogFactory import com.android.systemui.plugins.ActivityStarter @@ -101,7 +102,6 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var seamless: ViewGroup private lateinit var seamlessIcon: ImageView private lateinit var seamlessText: TextView - private lateinit var seamlessFallback: ImageView private lateinit var seekBar: SeekBar private lateinit var elapsedTimeView: TextView private lateinit var totalTimeView: TextView @@ -154,8 +154,6 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(holder.seamlessIcon).thenReturn(seamlessIcon) seamlessText = TextView(context) whenever(holder.seamlessText).thenReturn(seamlessText) - seamlessFallback = ImageView(context) - whenever(holder.seamlessFallback).thenReturn(seamlessFallback) seekBar = SeekBar(context) whenever(holder.seekBar).thenReturn(seekBar) elapsedTimeView = TextView(context) @@ -239,21 +237,19 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindDisabledDevice() { seamless.id = 1 - seamlessFallback.id = 2 + val fallbackString = context.getString(R.string.media_seamless_other_device) player.attachPlayer(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null) player.bindPlayer(state, PACKAGE) - verify(expandedSet).setVisibility(seamless.id, View.GONE) - verify(expandedSet).setVisibility(seamlessFallback.id, View.VISIBLE) - verify(collapsedSet).setVisibility(seamless.id, View.GONE) - verify(collapsedSet).setVisibility(seamlessFallback.id, View.VISIBLE) + assertThat(seamless.isEnabled()).isFalse() + assertThat(seamlessText.getText()).isEqualTo(fallbackString) + assertThat(seamless.contentDescription).isEqualTo(fallbackString) } @Test fun bindNullDevice() { - val fallbackString = context.getResources().getString( - com.android.internal.R.string.ext_media_seamless_action) + val fallbackString = context.getResources().getString(R.string.media_seamless_other_device) player.attachPlayer(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index a4d2348d03f1..cea30ed3e299 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -43,6 +43,7 @@ import static com.android.server.accessibility.AccessibilityTraceProto.WHERE; import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowTracing.WINSCOPE_EXT; import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.accessibilityservice.AccessibilityTrace; @@ -2081,7 +2082,7 @@ final class AccessibilityController { } private static final int BUFFER_CAPACITY = 1024 * 1024 * 12; - private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb"; + private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace" + WINSCOPE_EXT; private static final String TAG = "AccessibilityTracing"; private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java index 0bb97f560a1c..6204824d70a9 100644 --- a/services/core/java/com/android/server/wm/WindowTracing.java +++ b/services/core/java/com/android/server/wm/WindowTracing.java @@ -54,7 +54,8 @@ class WindowTracing { private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024; private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024; private static final int BUFFER_CAPACITY_ALL = 4096 * 1024; - private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb"; + static final String WINSCOPE_EXT = ".winscope"; + private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace" + WINSCOPE_EXT; private static final String TAG = "WindowTracing"; private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 8840057cc552..85ef8f73534c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -207,7 +207,7 @@ public class GameManagerServiceTests { } private void mockDeviceConfigInvalid() { - String configString = "mode=2,downscaleFactor=0.55"; + String configString = ""; when(DeviceConfig.getProperty(anyString(), anyString())) .thenReturn(configString); } @@ -373,11 +373,12 @@ public class GameManagerServiceTests { */ @Test public void testSetGameModePermissionDenied() { + mockModifyGameModeGranted(); + mockDeviceConfigAll(); GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); // Update the game mode so we can read back something valid. - mockModifyGameModeGranted(); gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1); assertEquals(GameManager.GAME_MODE_STANDARD, gameManagerService.getGameMode(mPackageName, USER_ID_1)); diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index 08c9e5dc7b64..2cc19438b927 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -36,30 +36,35 @@ fun FlickerTestParameter.navBarWindowIsVisible() { } } +/** + * If [allStates] is true, checks if the stack space of all displays is fully covered + * by any visible layer, during the whole transitions + * + * Otherwise, checks if the stack space of all displays is fully covered + * by any visible layer, at the start and end of the transition + * + * @param allStates if all states should be checked, othersie, just initial and final + */ @JvmOverloads -fun FlickerTestParameter.entireScreenCovered( - beginRotation: Int, - endRotation: Int = beginRotation, - allStates: Boolean = true -) { - val startingBounds = WindowUtils.getDisplayBounds(beginRotation) - val endingBounds = WindowUtils.getDisplayBounds(endRotation) +fun FlickerTestParameter.entireScreenCovered(allStates: Boolean = true) { if (allStates) { assertLayers { - if (startingBounds == endingBounds) { - this.coversAtLeast(startingBounds) - } else { - this.coversAtLeast(startingBounds) - .then() - .coversAtLeast(endingBounds) + this.invoke("entireScreenCovered") { entry -> + entry.entry.displays.forEach { display -> + entry.visibleRegion().coversAtLeast(display.layerStackSpace) + } } } } else { assertLayersStart { - this.visibleRegion().coversAtLeast(startingBounds) + this.entry.displays.forEach { display -> + this.visibleRegion().coversAtLeast(display.layerStackSpace) + } } assertLayersEnd { - this.visibleRegion().coversAtLeast(endingBounds) + this.entry.displays.forEach { display -> + this.visibleRegion().coversAtLeast(display.layerStackSpace) + } } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index f9e6babee938..1efb6daae31b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -123,9 +123,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) @Presubmit @Test - open fun entireScreenCovered() { - testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0) - } + open fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test @@ -152,4 +150,4 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) open fun launcherLayerReplacesApp() { testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT) } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt new file mode 100644 index 000000000000..3074e28b43fa --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 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 android.content.ComponentName +import android.support.test.launcherhelper.ILauncherStrategy +import android.support.test.launcherhelper.LauncherStrategyFactory +import com.android.server.wm.flicker.testapp.ActivityOptions + +class NonResizeableAppHelper @JvmOverloads constructor( + instr: Instrumentation, + launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME, + component: ComponentName = ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME, + launcherStrategy: ILauncherStrategy = LauncherStrategyFactory + .getInstance(instr) + .launcherStrategy +) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt new file mode 100644 index 000000000000..19fefb93b487 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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 android.content.ComponentName +import android.support.test.launcherhelper.ILauncherStrategy +import android.support.test.launcherhelper.LauncherStrategyFactory +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper + +class TwoActivitiesAppHelper @JvmOverloads constructor( + instr: Instrumentation, + launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME, + component: ComponentName = ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME, + launcherStrategy: ILauncherStrategy = LauncherStrategyFactory + .getInstance(instr) + .launcherStrategy +) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { + fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) { + val button = device.wait( + Until.findObject(By.res(getPackage(), "launch_second_activity")), + FIND_TIMEOUT) + + require(button != null) { + "Button not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)" + } + button.click() + wmHelper.waitForAppTransitionIdle() + wmHelper.waitForFullScreenApp(component) + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index 384d8e8e998d..d17e77d74c1c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -119,7 +119,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index ade215b3022d..6f0f55aa4888 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -116,8 +116,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation, - Surface.ROTATION_0) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index cdfcff3d4beb..6751439709bf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -115,7 +115,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test @@ -165,4 +165,4 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { .getConfigNonRotationTests(repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 05fc2672168e..8aaf9251018f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -125,8 +125,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation, - Surface.ROTATION_0) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index f35a180e1ad6..665204bc9e1e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -108,7 +108,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index 3bcf793e9071..fe1b1cd4795d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -143,8 +143,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test // During testing the launcher is always in portrait mode - fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation, - testSpec.config.endRotation) + fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt new file mode 100644 index 000000000000..42c252e70eea --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2021 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.launch + +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.entireScreenCovered +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.LAUNCHER_COMPONENT +import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test the back and forward transition between 2 activities. + * To run this test: `atest FlickerTests:ActivitiesTransitionTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) { + val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + eachRun { + testApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } + } + teardown { + test { + testApp.exit() + } + } + transitions { + testApp.openSecondActivity(device, wmHelper) + device.pressBack() + wmHelper.waitForAppTransitionIdle() + wmHelper.waitForFullScreenApp(testApp.component) + } + } + } + + @Presubmit + @Test + fun finishSubActivity() { + testSpec.assertWm { + this.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME) + .then() + .isAppWindowOnTop(ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME) + .then() + .isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME) + } + } + + @Presubmit + @Test + fun entireScreenCovered() = testSpec.entireScreenCovered() + + @Presubmit + @Test + fun launcherWindowNotVisible() { + testSpec.assertWm { + this.isAppWindowInvisible(LAUNCHER_COMPONENT, ignoreActivity = true) + } + } + + @Presubmit + @Test + fun launcherLayerNotVisible() { + testSpec.assertLayers { this.isInvisible(LAUNCHER_COMPONENT) } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index e6dc8523acbf..be919cd67c1e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -16,6 +16,8 @@ package com.android.server.wm.flicker.launch +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -33,8 +35,21 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test cold launch app from launcher. + * Test cold launching an app from launcher + * * To run this test: `atest FlickerTests:OpenAppColdTest` + * + * Actions: + * Make sure no apps are running on the device + * Launch an app [testApp] and wait animation to complete + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [OpenAppTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @@ -42,6 +57,9 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group1 class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) @@ -62,25 +80,46 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp } } + /** {@inheritDoc} */ @FlakyTest @Test override fun navBarLayerRotatesAndScales() { super.navBarLayerRotatesAndScales() } - @FlakyTest(bugId = 192721431) + /** {@inheritDoc} */ + @Postsubmit @Test - override fun appLayerReplacesLauncher() { - super.appLayerReplacesLauncher() - } + override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() - @FlakyTest(bugId = 192721431) + /** {@inheritDoc} */ + @Postsubmit @Test - override fun appWindowReplacesLauncherAsTopWindow() { + override fun appWindowReplacesLauncherAsTopWindow() = super.appWindowReplacesLauncherAsTopWindow() - } + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun navBarWindowIsVisible() = super.navBarWindowIsVisible() companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 7833e2f25eec..3678f33aa46e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.launch +import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -33,8 +34,23 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Launch an app from the recents app view (the overview) + * Test launching an app from the recents app view (the overview) + * * To run this test: `atest FlickerTests:OpenAppFromOverviewTest` + * + * Actions: + * Launch [testApp] + * Press recents + * Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to + * complete (only this action is traced) + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [OpenAppTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @@ -42,6 +58,9 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group1 class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) @@ -63,13 +82,38 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio } } + /** {@inheritDoc} */ @FlakyTest @Test - override fun navBarLayerRotatesAndScales() { - super.navBarLayerRotatesAndScales() - } + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun navBarWindowIsVisible() = super.navBarWindowIsVisible() companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt new file mode 100644 index 000000000000..b7176122095b --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2021 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.launch + +import android.content.ComponentName +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.FlakyTest +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper +import com.google.common.truth.Truth +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test launching an app while the device is locked + * + * To run this test: `atest FlickerTests:OpenAppNonResizeableTest` + * + * Actions: + * Lock the device. + * Launch an app on top of the lock screen [testApp] and wait animation to complete + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [OpenAppTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + override val testApp = NonResizeableAppHelper(instrumentation) + private val colorFadComponent = ComponentName("", "ColorFade BLAST#") + + /** + * Defines the transition used to run the test + */ + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { args -> + super.transition(this, args) + setup { + eachRun { + device.sleep() + wmHelper.waitFor("noAppWindowsOnTop") { + it.wmState.topVisibleAppWindow.isEmpty() + } + } + } + teardown { + eachRun { + testApp.exit(wmHelper) + } + } + transitions { + testApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } + } + + /** + * Checks that the nav bar layer starts visible, becomes invisible during unlocking animation + * and becomes visible at the end + */ + @Presubmit + @Test + fun navBarLayerVisibilityChanges() { + testSpec.assertLayers { + this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT) + .then() + .isInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT) + .then() + .isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT) + } + } + + /** + * Checks that the app layer doesn't exist at the start of the transition, that it is + * created (invisible) and becomes visible during the transition + */ + @Presubmit + @Test + fun appLayerBecomesVisible() { + testSpec.assertLayers { + this.notContains(testApp.component) + .then() + .isInvisible(testApp.component) + .then() + .isVisible(testApp.component) + } + } + + /** + * Checks that the app window doesn't exist at the start of the transition, that it is + * created (invisible - optional) and becomes visible during the transition + * + * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging, + * the window may be visible or not depending on what was processed until that moment. + */ + @Presubmit + @Test + fun appWindowBecomesVisible() { + testSpec.assertWm { + this.notContains(testApp.component) + .then() + .isAppWindowInvisible(testApp.component, + ignoreActivity = true, isOptional = true) + .then() + .isAppWindowVisible(testApp.component, ignoreActivity = true) + } + } + + /** + * Checks if [testApp] is visible at the end of the transition + */ + @Presubmit + @Test + fun appWindowBecomesVisibleAtEnd() { + testSpec.assertWmEnd { + this.isVisible(testApp.component) + } + } + + /** + * Checks that the nav bar starts the transition visible, then becomes invisible during + * then unlocking animation and becomes visible at the end of the transition + */ + @Postsubmit + @Test + fun navBarWindowsVisibilityChanges() { + testSpec.assertWm { + this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT) + .then() + .isNonAppWindowInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT) + .then() + .isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT) + } + } + + /** {@inheritDoc} */ + @FlakyTest + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + /** {@inheritDoc} */ + @FlakyTest + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun entireScreenCovered() = super.entireScreenCovered() + + /** + * Checks that the focus changes from the launcher to [testApp] + */ + @FlakyTest + @Test + override fun focusChanges() = super.focusChanges() + + /** + * Checks that the screen is locked at the start of the transition ([colorFadComponent]) + * layer is visible + */ + @Postsubmit + @Test + fun screenLockedStart() { + testSpec.assertLayersStart { + isVisible(colorFadComponent) + } + } + + /** + * This test checks if the launcher is visible at the start and the app at the end, + * it cannot use the regular assertion (check over time), because on lock screen neither + * the app not the launcher are visible, and there is no top visible window. + */ + @Postsubmit + @Test + override fun appWindowReplacesLauncherAsTopWindow() { + testSpec.assertWm { + this.invoke("noAppWindowsOnTop") { + Truth.assertWithMessage("Should not have any app window on top " + + "when the screen is locked") + .that(it.wmState.topVisibleAppWindow) + .isEmpty() + }.then() + .isAppWindowOnTop(testApp.component) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), + supportedRotations = listOf(Surface.ROTATION_0) + ) + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index 860a5aed13fd..14d17f82b805 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -40,12 +40,16 @@ import com.android.server.wm.flicker.statusBarLayerIsVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SPLASH_SCREEN_COMPONENT import org.junit.Test abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) + protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) + /** + * Defines the transition used to run the test + */ protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { withTestName { testSpec.name } repeat { testSpec.config.repetitions } @@ -62,6 +66,10 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } } + /** + * Entry point for the test runner. It will use this method to initialize and cache + * flicker executions + */ @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { @@ -69,42 +77,60 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } } - @Presubmit - @Test + /** + * Checks that the navigation bar window is visible during the whole transition + */ open fun navBarWindowIsVisible() { testSpec.navBarWindowIsVisible() } - @Presubmit - @Test + /** + * Checks that the navigation bar layer is visible during the whole transition + */ open fun navBarLayerIsVisible() { testSpec.navBarLayerIsVisible() } + /** + * Checks the position of the navigation bar at the start and end of the transition + */ @Presubmit @Test open fun navBarLayerRotatesAndScales() { testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) } + /** + * Checks that the status bar window is visible during the whole transition + */ @Presubmit @Test open fun statusBarWindowIsVisible() { testSpec.statusBarWindowIsVisible() } + /** + * Checks that the status bar layer is visible during the whole transition + */ @Presubmit @Test open fun statusBarLayerIsVisible() { testSpec.statusBarLayerIsVisible() } + /** + * Checks the position of the status bar at the start and end of the transition + */ @Presubmit @Test open fun statusBarLayerRotatesScales() { testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) } + /** + * Checks that all windows that are visible on the trace, are visible for at least 2 + * consecutive entries. + */ @Presubmit @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { @@ -113,6 +139,10 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } } + /** + * Checks that all layers that are visible on the trace, are visible for at least 2 + * consecutive entries. + */ @Presubmit @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { @@ -121,13 +151,16 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } } + /** + * Checks that all parts of the screen are covered during the transition + */ @Presubmit @Test - // During testing the launcher is always in portrait mode - open fun entireScreenCovered() { - testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation) - } + open fun entireScreenCovered() = testSpec.entireScreenCovered() + /** + * Checks that the focus changes from the launcher to [testApp] + */ @Presubmit @Test open fun focusChanges() { @@ -136,12 +169,19 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } } - @Presubmit - @Test + /** + * Checks that [LAUNCHER_COMPONENT] layer is visible at the start of the transition, and + * is replaced by [testApp], which remains visible until the end + */ open fun appLayerReplacesLauncher() { testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component) } + /** + * Checks that [LAUNCHER_COMPONENT] window is visible at the start of the transition, and + * is replaced by a snapshot or splash screen (optional), and finally, is replaced by + * [testApp], which remains visible until the end + */ @Presubmit @Test open fun appWindowReplacesLauncherAsTopWindow() { @@ -150,12 +190,16 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { .then() .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true) .then() + .isAppWindowOnTop(SPLASH_SCREEN_COMPONENT, isOptional = true) + .then() .isAppWindowOnTop(testApp.component) } } - @Presubmit - @Test + /** + * Checks that [LAUNCHER_COMPONENT] window is visible at the start, and + * becomes invisible during the transition + */ open fun launcherWindowBecomesInvisible() { testSpec.assertWm { this.isAppWindowVisible(LAUNCHER_COMPONENT) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index b509c61d2aba..5edee0cf0ca0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.launch +import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -32,8 +33,22 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test warm launch app. + * Test warm launching an app from launcher + * * To run this test: `atest FlickerTests:OpenAppWarmTest` + * + * Actions: + * Launch [testApp] + * Press home + * Relaunch an app [testApp] and wait animation to complete (only this action is traced) + * + * Notes: + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [OpenAppTransition] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup */ @RequiresDevice @RunWith(Parameterized::class) @@ -41,6 +56,9 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group1 class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + /** + * Defines the transition used to run the test + */ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { super.transition(this, it) @@ -65,11 +83,38 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp } } + /** {@inheritDoc} */ @FlakyTest @Test override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + /** {@inheritDoc} */ + @Presubmit + @Test + override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun navBarWindowIsVisible() = super.navBarWindowIsVisible() + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt new file mode 100644 index 000000000000..035aac1c5e86 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2021 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.quickswitch + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.RequiresDevice +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.LAUNCHER_COMPONENT +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.isRotated +import com.android.server.wm.flicker.navBarLayerIsVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsVisible +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsVisible +import com.android.server.wm.flicker.statusBarWindowIsVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switching back to previous app from last opened app + * + * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest` + * + * Actions: + * Launch an app [testApp1] + * Launch another app [testApp2] + * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1] + * + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + + private val testApp1 = SimpleAppHelper(instrumentation) + private val testApp2 = NonResizeableAppHelper(instrumentation) + + private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + testApp1.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp1.component) + + testApp2.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp2.component) + } + } + transitions { + // Swipe right from bottom to quick switch back + // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle + // as to not accidentally trigger a swipe back or forward action which would result + // in the same behavior but not testing quick swap. + device.swipe( + startDisplayBounds.bounds.right / 3, + startDisplayBounds.bounds.bottom, + 2 * startDisplayBounds.bounds.right / 3, + startDisplayBounds.bounds.bottom, + if (testSpec.config.startRotation.isRotated()) 75 else 30 + ) + + wmHelper.waitForFullScreenApp(testApp1.component) + wmHelper.waitForAppTransitionIdle() + } + + teardown { + test { + testApp1.exit() + testApp2.exit() + } + } + } + } + + /** + * Checks that the transition starts with [testApp2]'s windows filling/covering exactly the + * entirety of the display. + */ + @Postsubmit + @Test + fun startsWithApp2WindowsCoverFullScreen() { + testSpec.assertWmStart { + this.frameRegion(testApp2.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that the transition starts with [testApp2]'s layers filling/covering exactly the + * entirety of the display. + */ + @Postsubmit + @Test + fun startsWithApp2LayersCoverFullScreen() { + testSpec.assertLayersStart { + this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that the transition starts with [testApp2] being the top window. + */ + @Postsubmit + @Test + fun startsWithApp2WindowBeingOnTop() { + testSpec.assertWmStart { + this.isAppWindowOnTop(testApp2.component) + } + } + + /** + * Checks that [testApp1] windows fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from [testApp2] back to the [testApp1]. + */ + @Postsubmit + @Test + fun endsWithApp1WindowsCoveringFullScreen() { + testSpec.assertWmEnd { + this.frameRegion(testApp1.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that [testApp1] layers fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from [testApp2] back to the [testApp1]. + */ + @Postsubmit + @Test + fun endsWithApp1LayersCoveringFullScreen() { + testSpec.assertLayersEnd { + this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that [testApp1] is the top window at the end of the transition once we have fully quick + * switched from [testApp2] back to the [testApp1]. + */ + @Postsubmit + @Test + fun endsWithApp1BeingOnTop() { + testSpec.assertWmEnd { + this.isAppWindowOnTop(testApp1.component) + } + } + + /** + * Checks that [testApp1]'s window starts off invisible and becomes visible at some point before + * the end of the transition and then stays visible until the end of the transition. + */ + @Postsubmit + @Test + fun app1WindowBecomesAndStaysVisible() { + testSpec.assertWm { + this.isAppWindowInvisible(testApp1.component) + .then() + .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true) + .then() + .isAppWindowVisible(testApp1.component, ignoreActivity = true) + } + } + + /** + * Checks that [testApp1]'s layer starts off invisible and becomes visible at some point before + * the end of the transition and then stays visible until the end of the transition. + */ + @Postsubmit + @Test + fun app1LayerBecomesAndStaysVisible() { + testSpec.assertLayers { + this.isInvisible(testApp1.component) + .then() + .isVisible(testApp1.component) + } + } + + /** + * Checks that [testApp2]'s window starts off visible and becomes invisible at some point before + * the end of the transition and then stays invisible until the end of the transition. + */ + @Postsubmit + @Test + fun app2WindowBecomesAndStaysInvisible() { + testSpec.assertWm { + this.isAppWindowVisible(testApp2.component, ignoreActivity = true) + .then() + .isAppWindowInvisible(testApp2.component) + } + } + + /** + * Checks that [testApp2]'s layer starts off visible and becomes invisible at some point before + * the end of the transition and then stays invisible until the end of the transition. + */ + @Postsubmit + @Test + fun app2LayerBecomesAndStaysInvisible() { + testSpec.assertLayers { + this.isVisible(testApp2.component) + .then() + .isInvisible(testApp2.component) + } + } + + /** + * Checks that [testApp2]'s window is visible at least until [testApp1]'s window is visible. + * Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially + * visible. + */ + @Postsubmit + @Test + fun app1WindowIsVisibleOnceApp2WindowIsInvisible() { + testSpec.assertWm { + this.isAppWindowVisible(testApp2.component) + .then() + // TODO: Do we actually want to test this? Seems too implementation specific... + .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true) + .then() + .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true) + .then() + .isAppWindowVisible(testApp1.component) + } + } + + /** + * Checks that [testApp2]'s layer is visible at least until [testApp1]'s window is visible. + * Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially + * visible. + */ + @Postsubmit + @Test + fun app1LayerIsVisibleOnceApp2LayerIsInvisible() { + testSpec.assertLayers { + this.isVisible(testApp2.component) + .then() + .isVisible(LAUNCHER_COMPONENT, isOptional = true) + .then() + .isVisible(SNAPSHOT_COMPONENT, isOptional = true) + .then() + .isVisible(testApp1.component) + } + } + + /** + * Checks that the navbar window is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible() + + /** + * Checks that the navbar layer is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible() + + /** + * Checks that the navbar is always in the right position and covers the expected region. + * + * NOTE: This doesn't check that the navbar is visible or not. + */ + @Postsubmit + @Test + fun navbarIsAlwaysInRightPosition() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + + /** + * Checks that the status bar window is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible() + + /** + * Checks that the status bar layer is visible throughout the entire transition. + */ + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ), + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90) + ) + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt new file mode 100644 index 000000000000..ca8f8af2df94 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2021 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.quickswitch + +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresDevice +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.LAUNCHER_COMPONENT +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.entireScreenCovered +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.navBarLayerIsVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsVisible +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsVisible +import com.android.server.wm.flicker.statusBarWindowIsVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switching to last opened app from launcher + * + * To run this test: `atest FlickerTests:QuickSwitchFromLauncherTest` + * + * Actions: + * Launch an app + * Navigate home to show launcher + * Swipe right from the bottom of the screen to quick switch back to the app + * + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) + device.pressHome() + wmHelper.waitForHomeActivityVisible() + wmHelper.waitForWindowSurfaceDisappeared(testApp.component) + } + } + transitions { + // Swipe right from bottom to quick switch back + // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle + // as to not accidentally trigger a swipe back or forward action which would result + // in the same behavior but not testing quick swap. + device.swipe( + startDisplayBounds.bounds.right / 3, + startDisplayBounds.bounds.bottom, + 2 * startDisplayBounds.bounds.right / 3, + startDisplayBounds.bounds.bottom, + 50 + ) + + wmHelper.waitForFullScreenApp(testApp.component) + wmHelper.waitForAppTransitionIdle() + } + + teardown { + eachRun { + testApp.exit() + } + } + } + } + + /** + * Checks that [testApp] windows fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from the launcher back to the [testApp]. + */ + @Presubmit + @Test + fun endsWithAppWindowsCoveringFullScreen() { + testSpec.assertWmEnd { + this.frameRegion(testApp.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that [testApp] layers fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from the launcher back to the [testApp]. + */ + @Presubmit + @Test + fun endsWithAppLayersCoveringFullScreen() { + testSpec.assertLayersEnd { + this.visibleRegion(testApp.component).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that [testApp] is the top window at the end of the transition once we have fully quick + * switched from the launcher back to the [testApp]. + */ + @Presubmit + @Test + fun endsWithAppBeingOnTop() { + testSpec.assertWmEnd { + this.isAppWindowOnTop(testApp.component) + } + } + + /** + * Checks that the transition starts with the home activity being tagged as visible. + */ + @Presubmit + @Test + fun startsWithHomeActivityFlaggedVisible() { + testSpec.assertWmStart { + this.isHomeActivityVisible(true) + } + } + + /** + * Checks that the transition starts with the launcher windows filling/covering exactly the + * entirety of the display. + */ + @Presubmit + @Test + fun startsWithLauncherWindowsCoverFullScreen() { + testSpec.assertWmStart { + this.frameRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that the transition starts with the launcher layers filling/covering exactly the + * entirety of the display. + */ + @Presubmit + @Test + fun startsWithLauncherLayersCoverFullScreen() { + testSpec.assertLayersStart { + this.visibleRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds) + } + } + + /** + * Checks that the transition starts with the launcher being the top window. + */ + @Presubmit + @Test + fun startsWithLauncherBeingOnTop() { + testSpec.assertWmStart { + this.isAppWindowOnTop(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the transition ends with the home activity being flagged as not visible. By this + * point we should have quick switched away from the launcher back to the [testApp]. + */ + @Presubmit + @Test + fun endsWithHomeActivityFlaggedInvisible() { + testSpec.assertWmEnd { + this.isHomeActivityVisible(false) + } + } + + /** + * Checks that [testApp]'s window starts off invisible and becomes visible at some point before + * the end of the transition and then stays visible until the end of the transition. + */ + @Presubmit + @Test + fun appWindowBecomesAndStaysVisible() { + testSpec.assertWm { + this.isAppWindowInvisible(testApp.component, ignoreActivity = true) + .then() + .isAppWindowVisible(testApp.component, ignoreActivity = true) + } + } + + /** + * Checks that [testApp]'s layer starts off invisible and becomes visible at some point before + * the end of the transition and then stays visible until the end of the transition. + */ + @Presubmit + @Test + fun appLayerBecomesAndStaysVisible() { + testSpec.assertLayers { + this.isInvisible(testApp.component) + .then() + .isVisible(testApp.component) + } + } + + /** + * Checks that the launcher window starts off visible and becomes invisible at some point before + * the end of the transition and then stays invisible until the end of the transition. + */ + @Presubmit + @Test + fun launcherWindowBecomesAndStaysInvisible() { + testSpec.assertWm { + this.isAppWindowVisible(LAUNCHER_COMPONENT) + .then() + .isAppWindowInvisible(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the launcher layer starts off visible and becomes invisible at some point before + * the end of the transition and then stays invisible until the end of the transition. + */ + @Presubmit + @Test + fun launcherLayerBecomesAndStaysInvisible() { + testSpec.assertLayers { + this.isVisible(LAUNCHER_COMPONENT) + .then() + .isInvisible(LAUNCHER_COMPONENT) + } + } + + /** + * Checks that the launcher window is visible at least until the app window is visible. Ensures + * that at any point, either the launcher or [testApp] windows are at least partially visible. + */ + @Presubmit + @Test + fun appWindowIsVisibleOnceLauncherWindowIsInvisible() { + testSpec.assertWm { + this.isAppWindowVisible(LAUNCHER_COMPONENT) + .then() + .isAppWindowVisible(SNAPSHOT_COMPONENT) + .then() + .isAppWindowVisible(testApp.component) + } + } + + /** + * Checks that the launcher layer is visible at least until the app layer is visible. Ensures + * that at any point, either the launcher or [testApp] layers are at least partially visible. + */ + @Presubmit + @Test + fun appLayerIsVisibleOnceLauncherLayerIsInvisible() { + testSpec.assertLayers { + this.isVisible(LAUNCHER_COMPONENT) + .then() + .isVisible(SNAPSHOT_COMPONENT) + .then() + .isVisible(testApp.component) + } + } + + /** + * Checks that the navbar window is visible throughout the entire transition. + */ + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible() + + /** + * Checks that the navbar layer is visible throughout the entire transition. + */ + @Presubmit + @Test + fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible() + + /** + * Checks that the navbar is always in the right position and covers the expected region. + * + * NOTE: This doesn't check that the navbar is visible or not. + */ + @Presubmit + @Test + fun navbarIsAlwaysInRightPosition() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + + /** + * Checks that the status bar window is visible throughout the entire transition. + */ + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible() + + /** + * Checks that the status bar layer is visible throughout the entire transition. + */ + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible() + + /** + * Checks that the screen is always fully covered by visible layers throughout the transition. + */ + @Presubmit + @Test + fun screenIsAlwaysFilled() = testSpec.entireScreenCovered() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ), + // TODO: Test with 90 rotation + supportedRotations = listOf(Surface.ROTATION_0) + ) + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 73986b6def8f..d57c6698e35c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.rotation -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice @@ -85,7 +84,7 @@ class ChangeAppRotationTest( testSpec.statusBarWindowIsVisible() } - @Postsubmit + @Presubmit @Test fun statusBarLayerIsVisible() { testSpec.statusBarLayerIsVisible() @@ -98,30 +97,12 @@ class ChangeAppRotationTest( testSpec.config.startRotation, testSpec.config.endRotation) } - @Presubmit - @Test - override fun navBarWindowIsVisible() { - super.navBarWindowIsVisible() - } - - @Postsubmit - @Test - override fun navBarLayerIsVisible() { - super.navBarLayerIsVisible() - } - @FlakyTest @Test override fun navBarLayerRotatesAndScales() { super.navBarLayerRotatesAndScales() } - @Postsubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() { - super.visibleLayersShownMoreThanOneConsecutiveEntry() - } - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index 2b0b3c23a0b2..612ff9d3a153 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -25,7 +25,6 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerIsVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales @@ -39,8 +38,6 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) protected abstract val testApp: StandardAppHelper protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation) protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { setup { @@ -107,10 +104,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) @Presubmit @Test - open fun entireScreenCovered() { - testSpec.entireScreenCovered(testSpec.config.startRotation, - testSpec.config.endRotation, allStates = false) - } + open fun entireScreenCovered() = testSpec.entireScreenCovered() @Presubmit @Test @@ -124,7 +118,9 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) @Test open fun appLayerRotates_StartingPos() { testSpec.assertLayersStart { - this.visibleRegion(testApp.component).coversExactly(startingPos) + this.entry.displays.map { display -> + this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace) + } } } @@ -132,7 +128,9 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) @Test open fun appLayerRotates_EndingPos() { testSpec.assertLayersEnd { - this.visibleRegion(testApp.component).coversExactly(endingPos) + this.entry.displays.map { display -> + this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace) + } } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index b97b97768362..48efe73312c3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.rotation -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.WindowManager import androidx.test.filters.FlakyTest @@ -99,13 +98,15 @@ class SeamlessAppRotationTest( } } - @Postsubmit + @Presubmit @Test fun appLayerRotates() { testSpec.assertLayers { - this.coversExactly(startingPos, testApp.component) - .then() - .coversExactly(endingPos, testApp.component) + this.invoke("entireScreenCovered") { entry -> + entry.entry.displays.map { display -> + entry.visibleRegion(testApp.component).coversExactly(display.layerStackSpace) + } + } } } @@ -125,24 +126,6 @@ class SeamlessAppRotationTest( } } - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() { - super.visibleLayersShownMoreThanOneConsecutiveEntry() - } - - @Postsubmit - @Test - override fun navBarWindowIsVisible() { - super.navBarWindowIsVisible() - } - - @Postsubmit - @Test - override fun navBarLayerIsVisible() { - super.navBarLayerIsVisible() - } - @FlakyTest @Test override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 1599ed4b280f..3b9f33aaded1 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -59,5 +59,26 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <activity android:name=".NonResizeableActivity" + android:resizeableActivity="false" + android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity" + android:label="NonResizeableApp" + android:exported="true" + android:showOnLockScreen="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + <activity android:name=".ButtonActivity" + android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity" + android:configChanges="orientation|screenSize" + android:label="ButtonActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml new file mode 100644 index 000000000000..fe7bced690f9 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2018 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/holo_orange_light"> + <Button + android:id="@+id/launch_second_activity" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Second activity" /> +</LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml new file mode 100644 index 000000000000..6d5a9dd29248 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2021 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:background="@android:color/holo_orange_light"> + + <TextView + android:id="@+id/NonResizeableTest" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="center_vertical|center_horizontal" + android:text="NonResizeableActivity" + android:textAppearance="?android:attr/textAppearanceLarge"/> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 0ccc49897202..224d2ac38a11 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -41,4 +41,14 @@ public class ActivityOptions { public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".SimpleActivity"); + + public static final String NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME = "NonResizeableApp"; + public static final ComponentName NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME = + new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".NonResizeableActivity"); + + public static final String BUTTON_ACTIVITY_LAUNCHER_NAME = "ButtonApp"; + public static final ComponentName BUTTON_ACTIVITY_COMPONENT_NAME = + new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ButtonActivity"); } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java new file mode 100644 index 000000000000..b42ac2a6fd97 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 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.testapp; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.Button; + +public class ButtonActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + WindowManager.LayoutParams p = getWindow().getAttributes(); + p.layoutInDisplayCutoutMode = WindowManager.LayoutParams + .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + getWindow().setAttributes(p); + setContentView(R.layout.activity_button); + + Button button = findViewById(R.id.launch_second_activity); + button.setOnClickListener(v -> { + Intent intent = new Intent(ButtonActivity.this, SimpleActivity.class); + startActivity(intent); + }); + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java new file mode 100644 index 000000000000..61019d8b3716 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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.testapp; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.os.Bundle; + +public class NonResizeableActivity extends Activity { + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.activity_non_resizeable); + + setShowWhenLocked(true); + setTurnScreenOn(true); + KeyguardManager keyguardManager = getSystemService(KeyguardManager.class); + if (keyguardManager != null) { + keyguardManager.requestDismissKeyguard(this, null); + } + } +} |