diff options
10 files changed, 270 insertions, 67 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java index 39f861de1ba0..5cf9175073c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java @@ -50,7 +50,7 @@ class ActivityEmbeddingAnimationAdapter { final SurfaceControl mLeash; /** Area in absolute coordinate that the animation surface shouldn't go beyond. */ @NonNull - private final Rect mWholeAnimationBounds = new Rect(); + final Rect mWholeAnimationBounds = new Rect(); /** * Area in absolute coordinate that should represent all the content to show for this window. * This should be the end bounds for opening window, and start bounds for closing window in case @@ -229,20 +229,7 @@ class ActivityEmbeddingAnimationAdapter { mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y); t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); t.setAlpha(mLeash, mTransformation.getAlpha()); - - // The following applies an inverse scale to the clip-rect so that it crops "after" the - // scale instead of before. - mVecs[1] = mVecs[2] = 0; - mVecs[0] = mVecs[3] = 1; - mTransformation.getMatrix().mapVectors(mVecs); - mVecs[0] = 1.f / mVecs[0]; - mVecs[3] = 1.f / mVecs[3]; - final Rect clipRect = mTransformation.getClipRect(); - mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f); - mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f); - mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f); - mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f); - t.setCrop(mLeash, mRect); + t.setWindowCrop(mLeash, mWholeAnimationBounds.width(), mWholeAnimationBounds.height()); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index 1793a3d0feb4..4640106b5f1c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -26,7 +26,6 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; -import android.view.animation.ClipRectAnimation; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.ScaleAnimation; @@ -189,14 +188,6 @@ class ActivityEmbeddingAnimationSpec { startBounds.top - endBounds.top, 0); endTranslate.setDuration(CHANGE_ANIMATION_DURATION); endSet.addAnimation(endTranslate); - // The end leash is resizing, we should update the window crop based on the clip rect. - final Rect startClip = new Rect(startBounds); - final Rect endClip = new Rect(endBounds); - startClip.offsetTo(0, 0); - endClip.offsetTo(0, 0); - final Animation clipAnim = new ClipRectAnimation(startClip, endClip); - clipAnim.setDuration(CHANGE_ANIMATION_DURATION); - endSet.addAnimation(clipAnim); endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(), parentBounds.height()); endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting); diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt index 45cd65d9776c..45176448a9f4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt @@ -17,9 +17,12 @@ package com.android.server.wm.flicker.activityembedding import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.platform.test.annotations.Presubmit +import android.tools.common.traces.component.ComponentNameMatcher import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.Before +import org.junit.Test abstract class ActivityEmbeddingTestBase(flicker: LegacyFlickerTest) : BaseTest(flicker) { val testApp = ActivityEmbeddingAppHelper(instrumentation) @@ -29,4 +32,14 @@ abstract class ActivityEmbeddingTestBase(flicker: LegacyFlickerTest) : BaseTest( // The test should only be run on devices that support ActivityEmbedding. ActivityEmbeddingAppHelper.assumeActivityEmbeddingSupportedDevice() } + + /** Asserts the background animation layer is never visible during bounds change transition. */ + @Presubmit + @Test + fun backgroundLayerNeverVisible() { + val backgroundColorLayer = ComponentNameMatcher("", "Animation Background") + flicker.assertLayers { + isInvisible(backgroundColorLayer) + } + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt new file mode 100644 index 000000000000..badd876ae321 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt @@ -0,0 +1,152 @@ +/* + * 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.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 com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import androidx.test.filters.RequiresDevice +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test changing split ratio at runtime on a horizona split. + * + * Setup: Launch A|B in horizontal split with B being the secondary activity, by default A and B + * windows are equal in size. B is on the top and A is on the bottom. + * Transitions: + * Change the split ratio to A:B=0.7:0.3, expect bounds change for both A and B. + * + * To run this test: `atest FlickerTests:HorizontalSplitChangeRatioTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) : + ActivityEmbeddingTestBase(flicker) { + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivityHorizontally(wmHelper) + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds + ?: error("Display not found") + } + transitions { + testApp.changeSecondaryActivityRatio(wmHelper) + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } + } + + /** Assert the Main activity window is always visible. */ + @Presubmit + @Test + fun mainActivityWindowIsAlwaysVisible() { + flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + } + + /** Assert the Main activity window is always visible. */ + @Presubmit + @Test + fun mainActivityLayerIsAlwaysVisible() { + flicker.assertLayers { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + } + + /** Assert the Secondary activity window is always visible. */ + @Presubmit + @Test + fun secondaryActivityWindowIsAlwaysVisible() { + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } + } + + /** Assert the Secondary activity window is always visible. */ + @Presubmit + @Test + fun secondaryActivityLayerIsAlwaysVisible() { + flicker.assertLayers { isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } + } + + /** Assert the Main and Secondary activity change height during the transition. */ + @Presubmit + @Test + fun secondaryActivityAdjustsHeightRuntime() { + flicker.assertLayersStart { + val topLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + val bottomLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_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(topLayerRegion.region.height).isEqual(bottomLayerRegion.region.height) + check { "width" } + .that(topLayerRegion.region.width).isEqual(bottomLayerRegion.region.width) + topLayerRegion.notOverlaps(bottomLayerRegion.region) + // Layers of two activities sum to be fullscreen size on display. + topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds) + } + + flicker.assertLayersEnd { + val topLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + val bottomLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_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(topLayerRegion.region.height).isLower(bottomLayerRegion.region.height) + check { "height" } + .that( + topLayerRegion.region.height / 0.3f - + bottomLayerRegion.region.height / 0.7f) + .isLower(0.1f) + check { "width" } + .that(topLayerRegion.region.width).isEqual(bottomLayerRegion.region.width) + topLayerRegion.notOverlaps(bottomLayerRegion.region) + // Layers of two activities sum to be fullscreen size on display. + topLayerRegion.plus(bottomLayerRegion.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 diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt index 27de12e7dfdb..404f3290f04a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt @@ -142,14 +142,6 @@ class OpenThirdActivityOverSplitTest(flicker: LegacyFlickerTest) : } } - /** Assert the background animation layer is never visible during transition. */ - @Presubmit - @Test - fun backgroundLayerNeverVisible() { - val backgroundColorLayer = ComponentNameMatcher("", "Animation Background") - flicker.assertLayers { isInvisible(backgroundColorLayer) } - } - companion object { /** {@inheritDoc} */ private var startDisplayBounds = Rect.EMPTY 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 ade1491fa17b..883c7e6d5785 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 @@ -45,18 +45,22 @@ constructor( * based on the split pair rule. */ fun launchSecondaryActivity(wmHelper: WindowManagerStateHelper) { - val launchButton = - uiDevice.wait( - Until.findObject(By.res(getPackage(), "launch_secondary_activity_button")), - FIND_TIMEOUT - ) - require(launchButton != null) { "Can't find launch secondary activity button on screen." } - launchButton.click() - wmHelper - .StateSyncBuilder() - .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .waitForAndVerify() + launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_button") + } + + /** + * 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) { + launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_rtl_button") + } + + /** + * Clicks the button to launch the secondary activity in a horizontal split. + */ + fun launchSecondaryActivityHorizontally(wmHelper: WindowManagerStateHelper) { + launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_horizontally_button") } /** Clicks the button to launch a third activity over a secondary activity. */ @@ -101,16 +105,38 @@ constructor( */ fun finishSecondaryActivity(wmHelper: WindowManagerStateHelper) { val finishButton = - uiDevice.wait( - Until.findObject(By.res(getPackage(), "finish_secondary_activity_button")), - FIND_TIMEOUT - ) + uiDevice.wait( + Until.findObject(By.res(getPackage(), "finish_secondary_activity_button")), + FIND_TIMEOUT + ) require(finishButton != null) { "Can't find finish secondary activity button on screen." } finishButton.click() wmHelper - .StateSyncBuilder() - .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT) - .waitForAndVerify() + .StateSyncBuilder() + .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT) + .waitForAndVerify() + } + + /** + * Clicks the button to toggle the split ratio of secondary activity. + */ + fun changeSecondaryActivityRatio(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject( + By.res(getPackage(), + "toggle_split_ratio_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { + "Can't find toggle ratio for secondary activity button on screen." + } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .withTransitionSnapshotGone() + .waitForAndVerify() } fun secondaryActivityEnterPip(wmHelper: WindowManagerStateHelper) { @@ -149,25 +175,19 @@ constructor( .waitForAndVerify() } - /** - * 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) { + private fun launchSecondaryActivityFromButton( + wmHelper: WindowManagerStateHelper, buttonName: String) { val launchButton = - uiDevice.wait( - Until.findObject(By.res(getPackage(), "launch_secondary_activity_rtl_button")), - FIND_TIMEOUT - ) + uiDevice.wait(Until.findObject(By.res(getPackage(), buttonName)), FIND_TIMEOUT) require(launchButton != null) { - "Can't find launch secondary activity rtl button on screen." + "Can't find launch secondary activity button : " + buttonName + "on screen." } launchButton.click() wmHelper - .StateSyncBuilder() - .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .waitForAndVerify() + .StateSyncBuilder() + .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() } /** 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 e32a7092bf5d..86c21906163f 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 @@ -38,6 +38,14 @@ android:text="Launch Secondary Activity in RTL" /> <Button + android:id="@+id/launch_secondary_activity_horizontally_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="BOTTOM_TO_TOP" + android:text="Launch Secondary Activity Horizontally" /> + + <Button android:id="@+id/launch_placeholder_split_button" android:layout_width="wrap_content" android:layout_height="48dp" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml index 135140aa2377..6d4de995bd73 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml @@ -35,6 +35,14 @@ android:onClick="launchThirdActivity" android:text="Launch a third activity" /> + <ToggleButton + android:id="@+id/toggle_split_ratio_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:textOn="Ratio 0.5" + android:textOff="Ratio 0.3" + android:checked="false" /> + <Button android:id="@+id/secondary_enter_pip_button" android:layout_width="wrap_content" 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 3b1a8599f3e1..23fa91c37728 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 @@ -22,6 +22,7 @@ import android.content.Intent; import android.os.Bundle; import android.view.View; +import androidx.annotation.NonNull; import androidx.window.embedding.ActivityFilter; import androidx.window.embedding.ActivityRule; import androidx.window.embedding.EmbeddingAspectRatio; @@ -152,6 +153,9 @@ public class ActivityEmbeddingMainActivity extends Activity { if (layoutDirectionStr.equals(LayoutDirection.LEFT_TO_RIGHT.toString())) { return LayoutDirection.LEFT_TO_RIGHT; } + if (layoutDirectionStr.equals(LayoutDirection.BOTTOM_TO_TOP.toString())) { + return LayoutDirection.BOTTOM_TO_TOP; + } if (layoutDirectionStr.equals(LayoutDirection.RIGHT_TO_LEFT.toString())) { return LayoutDirection.RIGHT_TO_LEFT; } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java index ee087ef9be2c..29cbf01dc6da 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java @@ -22,6 +22,11 @@ import android.app.PictureInPictureParams; import android.graphics.Color; import android.os.Bundle; import android.view.View; +import android.widget.ToggleButton; + +import androidx.window.embedding.SplitAttributes; +import androidx.window.embedding.SplitAttributesCalculatorParams; +import androidx.window.embedding.SplitController; /** * Activity to be used as the secondary activity to split with @@ -29,18 +34,41 @@ import android.view.View; */ public class ActivityEmbeddingSecondaryActivity extends Activity { + private SplitController mSplitController; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_embedding_secondary_activity_layout); findViewById(R.id.secondary_activity_layout).setBackgroundColor(Color.YELLOW); findViewById(R.id.finish_secondary_activity_button).setOnClickListener( - new View.OnClickListener() { + new View.OnClickListener() { @Override public void onClick(View v) { finish(); } - }); + }); + mSplitController = SplitController.getInstance(this); + final ToggleButton splitRatio = findViewById(R.id.toggle_split_ratio_button); + mSplitController.setSplitAttributesCalculator(params -> { + return new SplitAttributes.Builder() + .setSplitType( + SplitAttributes.SplitType.ratio( + splitRatio.isChecked() ? 0.7f : 0.5f) + ) + .setLayoutDirection( + params.getDefaultSplitAttributes() + .getLayoutDirection()) + .build(); + }); + splitRatio.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + // This triggers a recalcuation of splitatributes. + mSplitController.invalidateTopVisibleSplitAttributes(); + } + }); findViewById(R.id.secondary_enter_pip_button).setOnClickListener( new View.OnClickListener() { @Override |