Loading tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm.flicker.activityembedding import android.platform.test.annotations.Presubmit import android.tools.common.datatypes.Rect import android.tools.common.datatypes.Region import android.tools.common.flicker.subject.region.RegionSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper 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 a trampoline activity and resulting in a split state. * * Setup: Launch Activity A in fullscreen. * * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and * finishes itself, end up in split A|B. * * To run this test: `atest FlickerTests:OpenTrampolineActivityTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) { override val transition: FlickerBuilder.() -> Unit = { setup { tapl.setExpectedRotationCheckEnabled(false) testApp.launchViaIntent(wmHelper) startDisplayBounds = wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Can't get display bounds") } transitions { testApp.launchTrampolineActivity(wmHelper) } teardown { tapl.goHome() testApp.exit(wmHelper) } } /** Assert the background animation layer is never visible during bounds change transition. */ @Presubmit @Test fun backgroundLayerNeverVisible() { val backgroundColorLayer = ComponentNameMatcher("", "Animation Background") flicker.assertLayers { isInvisible(backgroundColorLayer) } } /** Trampoline activity should finish itself before the end of this test. */ @Presubmit @Test fun trampolineActivityFinishes() { flicker.assertWmEnd { notContains(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT) } } @Presubmit @Test fun trampolineLayerNeverVisible() { flicker.assertLayers { isInvisible(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT) } } /** Main activity is always visible throughout this test. */ @Presubmit @Test fun mainActivityWindowAlwaysVisible() { flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } } // TODO(b/289140963): After this is fixed, assert the main Activity window is visible // throughout the test instead. /** Main activity layer is visible before and after the transition. */ @Presubmit @Test fun mainActivityLayerAlwaysVisible() { flicker.assertLayersStart { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } flicker.assertLayersEnd { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } } /** Secondary activity is launched from the trampoline activity. */ @Presubmit @Test fun secondaryActivityWindowLaunchedFromTrampoline() { flicker.assertWm { notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } } /** Secondary activity is launched from the trampoline activity. */ @Presubmit @Test fun secondaryActivityLayerLaunchedFromTrampoline() { flicker.assertLayers { isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } } /** Main activity should go from fullscreen to being a split with secondary activity. */ @Presubmit @Test fun mainActivityWindowGoesFromFullscreenToSplit() { flicker.assertWm { this.invoke("mainActivityStartsInFullscreen") { it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) .coversExactly(startDisplayBounds) } // Begin of transition. .then() .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .invoke("mainAndSecondaryInSplit") { val mainActivityRegion = RegionSubject( it.visibleRegion( ActivityEmbeddingAppHelper .MAIN_ACTIVITY_COMPONENT).region, it.timestamp) val secondaryActivityRegion = RegionSubject( it.visibleRegion( ActivityEmbeddingAppHelper .SECONDARY_ACTIVITY_COMPONENT).region, it.timestamp) check { "height" } .that(mainActivityRegion.region.height) .isEqual(secondaryActivityRegion.region.height) check { "width" } .that(mainActivityRegion.region.width) .isEqual(secondaryActivityRegion.region.width) mainActivityRegion .plus(secondaryActivityRegion.region) .coversExactly(startDisplayBounds) } } } /** Main activity should go from fullscreen to being a split with secondary activity. */ @Presubmit @Test fun mainActivityLayerGoesFromFullscreenToSplit() { flicker.assertLayers { this.invoke("mainActivityStartsInFullscreen") { it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) .coversExactly(startDisplayBounds) } .then() .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } flicker.assertLayersEnd { val leftLayerRegion = visibleRegion( ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) val rightLayerRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } .that(leftLayerRegion.region.height) .isEqual(rightLayerRegion.region.height) check { "width" } .that(leftLayerRegion.region.width) .isEqual(rightLayerRegion.region.width) leftLayerRegion.notOverlaps(rightLayerRegion.region) // Layers of two activities sum to be fullscreen size on display. leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds) } } companion object { /** {@inheritDoc} */ private var startDisplayBounds = Rect.EMPTY /** * Creates the test configurations. * * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } No newline at end of file tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +22 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,25 @@ constructor( .waitForAndVerify() } /** * Clicks the button to launch the trampoline activity, which should launch the secondary * activity and finish itself. */ fun launchTrampolineActivity(wmHelper: WindowManagerStateHelper) { val launchButton = uiDevice.wait( Until.findObject(By.res(getPackage(), "launch_trampoline_button")), FIND_TIMEOUT ) require(launchButton != null) { "Can't find launch trampoline activity button on screen." } launchButton.click() wmHelper .StateSyncBuilder() .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) .withActivityRemoved(TRAMPOLINE_ACTIVITY_COMPONENT) .waitForAndVerify() } /** * Clicks the button to finishes the secondary activity launched through * [launchSecondaryActivity], waits for the main activity to resume. Loading Loading @@ -197,6 +216,9 @@ constructor( ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT .toFlickerComponent() val TRAMPOLINE_ACTIVITY_COMPONENT = ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent() @JvmStatic fun getWindowExtensions(): WindowExtensions? { try { Loading tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -192,6 +192,14 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".ActivityEmbeddingTrampolineActivity" android:label="ActivityEmbedding Trampoline" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"> </activity> <activity android:name=".ActivityEmbeddingSecondaryActivity" android:label="ActivityEmbedding Secondary" Loading tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +8 −0 Original line number Diff line number Diff line Loading @@ -60,4 +60,12 @@ android:tag="RIGHT_TO_LEFT" android:text="Launch Placeholder Split in RTL" /> <Button android:id="@+id/launch_trampoline_button" android:layout_width="wrap_content" android:layout_height="48dp" android:onClick="launchTrampolineActivity" android:tag="LEFT_TO_RIGHT" android:text="Launch Trampoline Activity" /> </LinearLayout> tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java +10 −3 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ package com.android.server.wm.flicker.testapp; import androidx.annotation.NonNull; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper; import androidx.annotation.NonNull; import androidx.window.embedding.ActivityFilter; import androidx.window.embedding.ActivityRule; import androidx.window.embedding.EmbeddingAspectRatio; Loading Loading @@ -59,6 +57,15 @@ public class ActivityEmbeddingMainActivity extends Activity { mRuleController = RuleController.getInstance(this); } /** R.id.launch_trampoline_button onClick */ public void launchTrampolineActivity(View view) { final String layoutDirection = view.getTag().toString(); mRuleController.clearRules(); mRuleController.addRule(createSplitPairRules(layoutDirection)); startActivity(new Intent().setComponent( ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT)); } /** R.id.launch_secondary_activity_button onClick */ public void launchSecondaryActivity(View view) { final String layoutDirection = view.getTag().toString(); Loading Loading
tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm.flicker.activityembedding import android.platform.test.annotations.Presubmit import android.tools.common.datatypes.Rect import android.tools.common.datatypes.Region import android.tools.common.flicker.subject.region.RegionSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper 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 a trampoline activity and resulting in a split state. * * Setup: Launch Activity A in fullscreen. * * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and * finishes itself, end up in split A|B. * * To run this test: `atest FlickerTests:OpenTrampolineActivityTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) { override val transition: FlickerBuilder.() -> Unit = { setup { tapl.setExpectedRotationCheckEnabled(false) testApp.launchViaIntent(wmHelper) startDisplayBounds = wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Can't get display bounds") } transitions { testApp.launchTrampolineActivity(wmHelper) } teardown { tapl.goHome() testApp.exit(wmHelper) } } /** Assert the background animation layer is never visible during bounds change transition. */ @Presubmit @Test fun backgroundLayerNeverVisible() { val backgroundColorLayer = ComponentNameMatcher("", "Animation Background") flicker.assertLayers { isInvisible(backgroundColorLayer) } } /** Trampoline activity should finish itself before the end of this test. */ @Presubmit @Test fun trampolineActivityFinishes() { flicker.assertWmEnd { notContains(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT) } } @Presubmit @Test fun trampolineLayerNeverVisible() { flicker.assertLayers { isInvisible(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT) } } /** Main activity is always visible throughout this test. */ @Presubmit @Test fun mainActivityWindowAlwaysVisible() { flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } } // TODO(b/289140963): After this is fixed, assert the main Activity window is visible // throughout the test instead. /** Main activity layer is visible before and after the transition. */ @Presubmit @Test fun mainActivityLayerAlwaysVisible() { flicker.assertLayersStart { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } flicker.assertLayersEnd { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } } /** Secondary activity is launched from the trampoline activity. */ @Presubmit @Test fun secondaryActivityWindowLaunchedFromTrampoline() { flicker.assertWm { notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } } /** Secondary activity is launched from the trampoline activity. */ @Presubmit @Test fun secondaryActivityLayerLaunchedFromTrampoline() { flicker.assertLayers { isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } } /** Main activity should go from fullscreen to being a split with secondary activity. */ @Presubmit @Test fun mainActivityWindowGoesFromFullscreenToSplit() { flicker.assertWm { this.invoke("mainActivityStartsInFullscreen") { it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) .coversExactly(startDisplayBounds) } // Begin of transition. .then() .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .invoke("mainAndSecondaryInSplit") { val mainActivityRegion = RegionSubject( it.visibleRegion( ActivityEmbeddingAppHelper .MAIN_ACTIVITY_COMPONENT).region, it.timestamp) val secondaryActivityRegion = RegionSubject( it.visibleRegion( ActivityEmbeddingAppHelper .SECONDARY_ACTIVITY_COMPONENT).region, it.timestamp) check { "height" } .that(mainActivityRegion.region.height) .isEqual(secondaryActivityRegion.region.height) check { "width" } .that(mainActivityRegion.region.width) .isEqual(secondaryActivityRegion.region.width) mainActivityRegion .plus(secondaryActivityRegion.region) .coversExactly(startDisplayBounds) } } } /** Main activity should go from fullscreen to being a split with secondary activity. */ @Presubmit @Test fun mainActivityLayerGoesFromFullscreenToSplit() { flicker.assertLayers { this.invoke("mainActivityStartsInFullscreen") { it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) .coversExactly(startDisplayBounds) } .then() .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } flicker.assertLayersEnd { val leftLayerRegion = visibleRegion( ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) val rightLayerRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } .that(leftLayerRegion.region.height) .isEqual(rightLayerRegion.region.height) check { "width" } .that(leftLayerRegion.region.width) .isEqual(rightLayerRegion.region.width) leftLayerRegion.notOverlaps(rightLayerRegion.region) // Layers of two activities sum to be fullscreen size on display. leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds) } } companion object { /** {@inheritDoc} */ private var startDisplayBounds = Rect.EMPTY /** * Creates the test configurations. * * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } No newline at end of file
tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +22 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,25 @@ constructor( .waitForAndVerify() } /** * Clicks the button to launch the trampoline activity, which should launch the secondary * activity and finish itself. */ fun launchTrampolineActivity(wmHelper: WindowManagerStateHelper) { val launchButton = uiDevice.wait( Until.findObject(By.res(getPackage(), "launch_trampoline_button")), FIND_TIMEOUT ) require(launchButton != null) { "Can't find launch trampoline activity button on screen." } launchButton.click() wmHelper .StateSyncBuilder() .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) .withActivityRemoved(TRAMPOLINE_ACTIVITY_COMPONENT) .waitForAndVerify() } /** * Clicks the button to finishes the secondary activity launched through * [launchSecondaryActivity], waits for the main activity to resume. Loading Loading @@ -197,6 +216,9 @@ constructor( ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT .toFlickerComponent() val TRAMPOLINE_ACTIVITY_COMPONENT = ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent() @JvmStatic fun getWindowExtensions(): WindowExtensions? { try { Loading
tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -192,6 +192,14 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".ActivityEmbeddingTrampolineActivity" android:label="ActivityEmbedding Trampoline" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"> </activity> <activity android:name=".ActivityEmbeddingSecondaryActivity" android:label="ActivityEmbedding Secondary" Loading
tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +8 −0 Original line number Diff line number Diff line Loading @@ -60,4 +60,12 @@ android:tag="RIGHT_TO_LEFT" android:text="Launch Placeholder Split in RTL" /> <Button android:id="@+id/launch_trampoline_button" android:layout_width="wrap_content" android:layout_height="48dp" android:onClick="launchTrampolineActivity" android:tag="LEFT_TO_RIGHT" android:text="Launch Trampoline Activity" /> </LinearLayout>
tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java +10 −3 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ package com.android.server.wm.flicker.testapp; import androidx.annotation.NonNull; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper; import androidx.annotation.NonNull; import androidx.window.embedding.ActivityFilter; import androidx.window.embedding.ActivityRule; import androidx.window.embedding.EmbeddingAspectRatio; Loading Loading @@ -59,6 +57,15 @@ public class ActivityEmbeddingMainActivity extends Activity { mRuleController = RuleController.getInstance(this); } /** R.id.launch_trampoline_button onClick */ public void launchTrampolineActivity(View view) { final String layoutDirection = view.getTag().toString(); mRuleController.clearRules(); mRuleController.addRule(createSplitPairRules(layoutDirection)); startActivity(new Intent().setComponent( ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT)); } /** R.id.launch_secondary_activity_button onClick */ public void launchSecondaryActivity(View view) { final String layoutDirection = view.getTag().toString(); Loading