Merge "[AE Flicker Test] From Activity A launch a trampoline Activity, expect to finish in a split A|B" into udc-qpr-dev

This commit is contained in:
An An Yu
2023-07-10 18:54:29 +00:00
committed by Android (Google) Code Review
7 changed files with 318 additions and 3 deletions

View File

@@ -0,0 +1,228 @@
/*
* 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()
}
}

View File

@@ -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.
@@ -197,6 +216,9 @@ constructor(
ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT
.toFlickerComponent()
val TRAMPOLINE_ACTIVITY_COMPONENT =
ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent()
@JvmStatic
fun getWindowExtensions(): WindowExtensions? {
try {

View File

@@ -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"

View File

@@ -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>

View File

@@ -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;
@@ -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();

View File

@@ -0,0 +1,36 @@
/*
* 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.testapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
/**
* A Trampoline Activity that launches {@link ActivityEmbeddingSecondaryActivity} and then
* finishes itself.
*/
public class ActivityEmbeddingTrampolineActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Trampoline activity doesn't have a view.
startActivity(new Intent().setComponent(
ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT));
finish();
}
}

View File

@@ -122,6 +122,12 @@ public class ActivityOptions {
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
}
public static class TrampolineActivity {
public static final String LABEL = "ActivityEmbeddingTrampolineActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingTrampolineActivity");
}
}
public static class Notification {