diff options
5 files changed, 354 insertions, 50 deletions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt new file mode 100644 index 000000000000..236c44e89f41 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt @@ -0,0 +1,211 @@ +/* + * 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.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +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 placeholder split over a normal split, both splits are configured in RTL. + * + * Setup: From A launch a split in RTL - resulting in B|A. + * Transitions: + * From A start PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. + * Expect split PlaceholderSecondary|PlaceholderPrimary covering split B|A. + * + * To run this test: `atest FlickerTests:RTLStartSecondaryWithPlaceholderTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class RTLStartSecondaryWithPlaceholderTest(flicker: FlickerTest) : + ActivityEmbeddingTestBase(flicker) { + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivityRTL(wmHelper) + } + transitions { + testApp.launchPlaceholderSplitRTL(wmHelper) + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } + } + + /** + * Main activity and Secondary activity will become invisible because they are covered by + * PlaceholderPrimary activity and PlaceholderSecondary activity. + */ + @Presubmit + @Test + fun assertWindowVisibilities() { + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + flicker.assertWm { + isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } + } + + /** + * Main activity and Secondary activity will become invisible because they are covered by + * PlaceholderPrimary activity and PlaceholderSecondary activity. + */ + @Presubmit + @Test + fun assertLayerVisibilities() { + flicker.assertLayers { + this.isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertLayers { + this.isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } + } + + /** Main activity and Secondary activity split is in right-to-left layout direction. */ + @Presubmit + @Test + fun assertWMRTLBeforeTransition() { + flicker.assertWmStart { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + /** Main activity and Secondary activity split is in right-to-left layout direction. */ + @Presubmit + @Test + fun assertLayerRTLBeforeTransition() { + flicker.assertLayersStart { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + /** + * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left + * layout direction. + */ + @Presubmit + @Test + fun assertWMRTLAfterTransition() { + flicker.assertWmEnd { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + /** + * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left + * layout direction. + */ + @Presubmit + @Test + fun assertLayerRTLAfterTransition() { + flicker.assertLayersEnd { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // Placeholder secondary activity is on the left, placeholder primary activity is on the + // right. + check { "isRTLAfterTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index a21965e0d7d5..793c68ea376d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -101,6 +101,29 @@ constructor( } /** + * Clicks the button to launch the secondary activity in RTL, which should split with the main + * activity based on the split pair rule. + */ + fun launchSecondaryActivityRTL(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject( + By.res(getPackage(), + "launch_secondary_activity_rtl_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { + "Can't find launch secondary activity rtl button on screen." + } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() + } + + /** * Clicks the button to launch the placeholder primary activity, which should launch the * placeholder secondary activity based on the placeholder rule. */ @@ -119,6 +142,29 @@ constructor( .waitForAndVerify() } + /** + * Clicks the button to launch the placeholder primary activity in RTL, which should launch the + * placeholder secondary activity based on the placeholder rule. + */ + fun launchPlaceholderSplitRTL(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject( + By.res(getPackage(), + "launch_placeholder_split_rtl_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { + "Can't find launch placeholder split button on screen." + } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() + } + companion object { private const val TAG = "ActivityEmbeddingAppHelper" diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index dc9ff3b01822..64302831202e 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -22,7 +22,9 @@ <application android:allowBackup="false" android:supportsRtl="true"> <uses-library android:name="androidx.window.extensions" android:required="false"/> - + <property + android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" + android:value="true" /> <activity android:name=".SimpleActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity" android:theme="@style/CutoutShortEdges" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml index f5241cae8fa8..b9d789b73732 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -25,24 +25,39 @@ android:id="@+id/launch_secondary_activity_button" android:layout_width="wrap_content" android:layout_height="48dp" - android:layout_centerHorizontal="true" android:onClick="launchSecondaryActivity" + android:tag="LEFT_TO_RIGHT" android:text="Launch Secondary Activity" /> <Button + android:id="@+id/launch_secondary_activity_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Secondary Activity in RTL" /> + + <Button android:id="@+id/launch_placeholder_split_button" android:layout_width="wrap_content" android:layout_height="48dp" - android:layout_centerHorizontal="true" android:onClick="launchPlaceholderSplit" + android:tag="LEFT_TO_RIGHT" android:text="Launch Placeholder Split" /> <Button android:id="@+id/launch_always_expand_activity_button" android:layout_width="wrap_content" android:layout_height="48dp" - android:layout_centerHorizontal="true" android:onClick="launchAlwaysExpandActivity" android:text="Launch Always Expand Activity" /> + <Button + android:id="@+id/launch_placeholder_split_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchPlaceholderSplit" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Placeholder Split in RTL" /> + </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java index 61202545f407..817c79c9831f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java @@ -16,22 +16,25 @@ package com.android.server.wm.flicker.testapp; + import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.util.ArraySet; -import android.util.Log; 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; import androidx.window.embedding.RuleController; -import androidx.window.extensions.embedding.ActivityEmbeddingComponent; -import androidx.window.extensions.embedding.EmbeddingRule; -import androidx.window.extensions.embedding.SplitPairRule; -import androidx.window.extensions.embedding.SplitPlaceholderRule; - -import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper; +import androidx.window.embedding.SplitAttributes; +import androidx.window.embedding.SplitAttributes.LayoutDirection; +import androidx.window.embedding.SplitController; +import androidx.window.embedding.SplitPairFilter; +import androidx.window.embedding.SplitPairRule; +import androidx.window.embedding.SplitPlaceholderRule; +import androidx.window.embedding.SplitRule; import java.util.HashSet; import java.util.Set; @@ -40,16 +43,27 @@ import java.util.Set; public class ActivityEmbeddingMainActivity extends Activity { private static final String TAG = "ActivityEmbeddingMainActivity"; private static final float DEFAULT_SPLIT_RATIO = 0.5f; + private RuleController mRuleController; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_embedding_main_layout); + final SplitController.SplitSupportStatus status = SplitController.getInstance( + this).getSplitSupportStatus(); + if (status != SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { + throw new RuntimeException( + "Unable to initiate SplitController in ActivityEmbeddingMainActivity, " + + "splitSupportStatus = " + status); + } + mRuleController = RuleController.getInstance(this); } /** R.id.launch_secondary_activity_button onClick */ public void launchSecondaryActivity(View view) { - initializeSplitRules(createSplitPairRules()); + final String layoutDirection = view.getTag().toString(); + mRuleController.clearRules(); + mRuleController.addRule(createSplitPairRules(layoutDirection)); startActivity(new Intent().setComponent( ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT)); } @@ -73,51 +87,67 @@ public class ActivityEmbeddingMainActivity extends Activity { /** R.id.launch_placeholder_split_button onClick */ public void launchPlaceholderSplit(View view) { - initializeSplitRules(createSplitPlaceholderRules()); + final String layoutDirection = view.getTag().toString(); + mRuleController.clearRules(); + mRuleController.addRule(createSplitPlaceholderRules(layoutDirection)); startActivity(new Intent().setComponent( ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT)); } - private void initializeSplitRules(Set<EmbeddingRule> rules) { - ActivityEmbeddingComponent embeddingComponent = - ActivityEmbeddingAppHelper.getActivityEmbeddingComponent(); - if (embeddingComponent == null) { - // Embedding not supported - Log.d(TAG, "ActivityEmbedding is not supported on this device"); - finish(); - return; - } - embeddingComponent.setEmbeddingRules(rules); + private static SplitPairRule createSplitPairRules(@NonNull String layoutDirection) { + final Set<SplitPairFilter> pairFilters = new HashSet<>(); + final SplitPairFilter activitiesPair = new SplitPairFilter( + ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT, + ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT, + null /* secondaryActivityIntentAction */); + pairFilters.add(activitiesPair); + final SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.SPLIT_TYPE_EQUAL) + .setLayoutDirection(parseLayoutDirection(layoutDirection)) + .build(); + // Setting thresholds to ALWAYS_ALLOW values to make it easy for running on all devices. + final SplitPairRule rule = new SplitPairRule.Builder(pairFilters) + .setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) + .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW) + .build(); + return rule; } - private Set<EmbeddingRule> createSplitPairRules() { - final Set<EmbeddingRule> rules = new ArraySet<>(); - final SplitPairRule rule = new SplitPairRule.Builder( - activitiesPair -> activitiesPair.first instanceof ActivityEmbeddingMainActivity - && activitiesPair.second instanceof ActivityEmbeddingSecondaryActivity, - activityIntentPair -> - activityIntentPair.first instanceof ActivityEmbeddingMainActivity - && activityIntentPair.second.getComponent().equals(ActivityOptions - .ActivityEmbedding.SecondaryActivity.COMPONENT), - windowMetrics -> true) - .setSplitRatio(DEFAULT_SPLIT_RATIO) + private static SplitPlaceholderRule createSplitPlaceholderRules( + @NonNull String layoutDirection) { + final Set<ActivityFilter> activityFilters = new HashSet<>(); + activityFilters.add(new ActivityFilter( + ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT, + null /* intentAction */)); + final Intent intent = new Intent(); + intent.setComponent( + ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT); + final SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.SPLIT_TYPE_EQUAL) + .setLayoutDirection(parseLayoutDirection(layoutDirection)) + .build(); + final SplitPlaceholderRule rule = new SplitPlaceholderRule.Builder(activityFilters, intent) + .setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) + .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW) .build(); - rules.add(rule); - return rules; + return rule; } - private Set<EmbeddingRule> createSplitPlaceholderRules() { - final Set<EmbeddingRule> rules = new ArraySet<>(); - final SplitPlaceholderRule rule = new SplitPlaceholderRule.Builder( - new Intent().setComponent( - ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT), - activity -> activity instanceof ActivityEmbeddingPlaceholderPrimaryActivity, - intent -> intent.getComponent().equals( - ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT), - windowMetrics -> true) - .setSplitRatio(DEFAULT_SPLIT_RATIO) - .build(); - rules.add(rule); - return rules; + private static LayoutDirection parseLayoutDirection(@NonNull String layoutDirectionStr) { + if (layoutDirectionStr.equals(LayoutDirection.LEFT_TO_RIGHT.toString())) { + return LayoutDirection.LEFT_TO_RIGHT; + } + if (layoutDirectionStr.equals(LayoutDirection.RIGHT_TO_LEFT.toString())) { + return LayoutDirection.RIGHT_TO_LEFT; + } + return LayoutDirection.LOCALE; } } |