From 328149549807b0581cfdf51adf02307788497061 Mon Sep 17 00:00:00 2001 From: Charles Chen <charlesccchen@google.com> Date: Thu, 23 May 2024 20:50:47 +0800 Subject: [PATCH] Move AnimationOptions to Change There are several reasons for this requests: 1. Fix "Dim flicker on package installer" 2. Implement overlay transition by animation resources override 3. Activity Embedding Animation Customization Test: atest TransitionTests ActivityEmbeddingAnimationRunnerTests ActivityEmbeddingControllerTests Bug: 327332488 Bug: 295805497 Flag: com.android.window.flags.move_animation_options_to_change Change-Id: I060b87506a1bf732228ec6b44e7e9dd48782ec0c --- core/java/android/window/TransitionInfo.java | 79 ++++++++++++++++++- .../ActivityEmbeddingAnimationSpec.java | 15 +++- .../ActivityEmbeddingController.java | 41 +++++++--- .../transition/DefaultTransitionHandler.java | 16 +++- .../transition/TransitionAnimationHelper.java | 10 ++- ...ActivityEmbeddingAnimationRunnerTests.java | 32 +++++++- .../ActivityEmbeddingControllerTests.java | 42 +++++++++- .../com/android/server/wm/Transition.java | 70 ++++++++++++---- .../android/server/wm/TransitionTests.java | 7 ++ 9 files changed, 271 insertions(+), 41 deletions(-) diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 4ffd88093531..8a79754398db 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -39,6 +39,7 @@ import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionType; import static android.view.WindowManager.transitTypeToString; +import android.annotation.AnimRes; import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; @@ -54,6 +55,8 @@ import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; +import com.android.window.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -211,6 +214,8 @@ public final class TransitionInfo implements Parcelable { private final ArrayList<Change> mChanges = new ArrayList<>(); private final ArrayList<Root> mRoots = new ArrayList<>(); + // TODO(b/327332488): Clean-up usages after the flag is fully enabled. + @Deprecated private AnimationOptions mOptions; /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */ @@ -275,7 +280,15 @@ public final class TransitionInfo implements Parcelable { mRoots.add(other); } + /** + * @deprecated Set {@link AnimationOptions} to change. This method is only used if + * {@link Flags#FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE} is disabled. + */ + @Deprecated public void setAnimationOptions(@Nullable AnimationOptions options) { + if (Flags.moveAnimationOptionsToChange()) { + return; + } mOptions = options; } @@ -340,6 +353,11 @@ public final class TransitionInfo implements Parcelable { return mRoots.get(0).mLeash; } + /** + * @deprecated Use {@link Change#getAnimationOptions()} instead. This method is called only + * if {@link Flags#FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE} is disabled. + */ + @Deprecated @Nullable public AnimationOptions getAnimationOptions() { return mOptions; @@ -661,6 +679,7 @@ public final class TransitionInfo implements Parcelable { private SurfaceControl mSnapshot = null; private float mSnapshotLuma; private ComponentName mActivityComponent = null; + private AnimationOptions mAnimationOptions = null; public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { mContainer = container; @@ -690,6 +709,7 @@ public final class TransitionInfo implements Parcelable { mSnapshot = in.readTypedObject(SurfaceControl.CREATOR); mSnapshotLuma = in.readFloat(); mActivityComponent = in.readTypedObject(ComponentName.CREATOR); + mAnimationOptions = in.readTypedObject(AnimationOptions.CREATOR); } private Change localRemoteCopy() { @@ -713,6 +733,7 @@ public final class TransitionInfo implements Parcelable { out.mSnapshot = mSnapshot != null ? new SurfaceControl(mSnapshot, "localRemote") : null; out.mSnapshotLuma = mSnapshotLuma; out.mActivityComponent = mActivityComponent; + out.mAnimationOptions = mAnimationOptions; return out; } @@ -813,6 +834,16 @@ public final class TransitionInfo implements Parcelable { mActivityComponent = component; } + /** + * Sets {@link AnimationOptions} to override animation. + */ + public void setAnimationOptions(@Nullable AnimationOptions options) { + if (!Flags.moveAnimationOptionsToChange()) { + return; + } + mAnimationOptions = options; + } + /** @return the container that is changing. May be null if non-remotable (eg. activity) */ @Nullable public WindowContainerToken getContainer() { @@ -952,6 +983,14 @@ public final class TransitionInfo implements Parcelable { return mActivityComponent; } + /** + * Returns the {@link AnimationOptions}. + */ + @Nullable + public AnimationOptions getAnimationOptions() { + return mAnimationOptions; + } + /** @hide */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { @@ -976,6 +1015,7 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mSnapshot, flags); dest.writeFloat(mSnapshotLuma); dest.writeTypedObject(mActivityComponent, flags); + dest.writeTypedObject(mAnimationOptions, flags); } @NonNull @@ -1028,6 +1068,9 @@ public final class TransitionInfo implements Parcelable { if (mEndFixedRotation != ROTATION_UNDEFINED) { sb.append(" endFixedRotation="); sb.append(mEndFixedRotation); } + if (mBackgroundColor != 0) { + sb.append(" bc=").append(Integer.toHexString(mBackgroundColor)); + } if (mSnapshot != null) { sb.append(" snapshot="); sb.append(mSnapshot); } @@ -1042,6 +1085,9 @@ public final class TransitionInfo implements Parcelable { sb.append(" taskParent="); sb.append(mTaskInfo.parentTaskId); } + if (mAnimationOptions != null) { + sb.append(" opt=").append(mAnimationOptions); + } sb.append('}'); return sb.toString(); } @@ -1051,14 +1097,23 @@ public final class TransitionInfo implements Parcelable { @SuppressWarnings("UserHandleName") public static final class AnimationOptions implements Parcelable { + /** + * The default value for animation resources ID, which means to use the system default + * animation. + */ + @SuppressWarnings("ResourceType") // Use as a hint to use the system default animation. + @AnimRes + public static final int DEFAULT_ANIMATION_RESOURCES_ID = 0xFFFFFFFF; + private int mType; - private int mEnterResId; - private int mExitResId; + private @AnimRes int mEnterResId = DEFAULT_ANIMATION_RESOURCES_ID; + private @AnimRes int mExitResId = DEFAULT_ANIMATION_RESOURCES_ID; private boolean mOverrideTaskTransition; private String mPackageName; private final Rect mTransitionBounds = new Rect(); private HardwareBuffer mThumbnail; private int mAnimations; + // TODO(b/295805497): Extract it from AnimationOptions private @ColorInt int mBackgroundColor; // Customize activity transition animation private CustomActivityTransition mCustomActivityOpenTransition; @@ -1121,10 +1176,18 @@ public final class TransitionInfo implements Parcelable { customTransition.addCustomActivityTransition(enterResId, exitResId, backgroundColor); } - /** Make options for a custom animation based on anim resources */ + /** + * Make options for a custom animation based on anim resources. + * + * @param packageName the package name to find the animation resources + * @param enterResId the open animation resources ID + * @param exitResId the close animation resources ID + * @param backgroundColor the background color + * @param overrideTaskTransition whether to override the task transition + */ @NonNull public static AnimationOptions makeCustomAnimOptions(@NonNull String packageName, - int enterResId, int exitResId, @ColorInt int backgroundColor, + @AnimRes int enterResId, @AnimRes int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) { AnimationOptions options = new AnimationOptions(ANIM_CUSTOM); options.mPackageName = packageName; @@ -1182,10 +1245,12 @@ public final class TransitionInfo implements Parcelable { return mType; } + @AnimRes public int getEnterResId() { return mEnterResId; } + @AnimRes public int getExitResId() { return mExitResId; } @@ -1284,6 +1349,12 @@ public final class TransitionInfo implements Parcelable { if (!mTransitionBounds.isEmpty()) { sb.append(" bounds=").append(mTransitionBounds); } + if (mEnterResId != DEFAULT_ANIMATION_RESOURCES_ID) { + sb.append(" enterResId=").append(mEnterResId); + } + if (mExitResId != DEFAULT_ANIMATION_RESOURCES_ID) { + sb.append(" exitResId=").append(mExitResId); + } sb.append('}'); return sb.toString(); } 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 0272f1cda6ef..b9868629e64b 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 @@ -38,6 +38,7 @@ import android.view.animation.TranslateAnimation; import android.window.TransitionInfo; import com.android.internal.policy.TransitionAnimation; +import com.android.window.flags.Flags; import com.android.wm.shell.shared.TransitionUtil; /** Animation spec for ActivityEmbedding transition. */ @@ -202,7 +203,7 @@ class ActivityEmbeddingAnimationSpec { Animation loadOpenAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); - final Animation customAnimation = loadCustomAnimation(info, isEnter); + final Animation customAnimation = loadCustomAnimation(info, change, isEnter); final Animation animation; if (customAnimation != null) { animation = customAnimation; @@ -229,7 +230,7 @@ class ActivityEmbeddingAnimationSpec { Animation loadCloseAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); - final Animation customAnimation = loadCustomAnimation(info, isEnter); + final Animation customAnimation = loadCustomAnimation(info, change, isEnter); final Animation animation; if (customAnimation != null) { animation = customAnimation; @@ -261,8 +262,14 @@ class ActivityEmbeddingAnimationSpec { } @Nullable - private Animation loadCustomAnimation(@NonNull TransitionInfo info, boolean isEnter) { - final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + private Animation loadCustomAnimation(@NonNull TransitionInfo info, + @NonNull TransitionInfo.Change change, boolean isEnter) { + final TransitionInfo.AnimationOptions options; + if (Flags.moveAnimationOptionsToChange()) { + options = change.getAnimationOptions(); + } else { + options = info.getAnimationOptions(); + } if (options == null || options.getType() != ANIM_CUSTOM) { return null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java index d6b9d34c5ab3..b4ef9f0fc2ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java @@ -32,6 +32,7 @@ import android.os.IBinder; import android.util.ArrayMap; import android.view.SurfaceControl; import android.window.TransitionInfo; +import android.window.TransitionInfo.AnimationOptions; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; @@ -39,6 +40,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import com.android.wm.shell.shared.TransitionUtil; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -117,24 +119,39 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle return false; } - final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); - if (options != null) { - // Scene-transition should be handled by app side. - if (options.getType() == ANIM_SCENE_TRANSITION) { + return shouldAnimateAnimationOptions(info); + } + + private boolean shouldAnimateAnimationOptions(@NonNull TransitionInfo info) { + if (!Flags.moveAnimationOptionsToChange()) { + return shouldAnimateAnimationOptions(info.getAnimationOptions()); + } + for (TransitionInfo.Change change : info.getChanges()) { + if (!shouldAnimateAnimationOptions(change.getAnimationOptions())) { + // If any of override animation is not supported, don't animate the transition. return false; } - // The case of ActivityOptions#makeCustomAnimation, Activity#overridePendingTransition, - // and Activity#overrideActivityTransition are supported. - if (options.getType() == ANIM_CUSTOM) { - return true; - } - // Use default transition handler to animate other override animation. - return !isSupportedOverrideAnimation(options); } - return true; } + private boolean shouldAnimateAnimationOptions(@Nullable AnimationOptions options) { + if (options == null) { + return true; + } + // Scene-transition should be handled by app side. + if (options.getType() == ANIM_SCENE_TRANSITION) { + return false; + } + // The case of ActivityOptions#makeCustomAnimation, Activity#overridePendingTransition, + // and Activity#overrideActivityTransition are supported. + if (options.getType() == ANIM_CUSTOM) { + return true; + } + // Use default transition handler to animate other override animation. + return !isSupportedOverrideAnimation(options); + } + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 2d6ba6ee7217..018c9044e2f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -103,6 +103,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; +import com.android.window.flags.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; @@ -543,7 +544,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { mTransactionPool, mMainExecutor, animRelOffset, cornerRadius, clipRect); - if (info.getAnimationOptions() != null) { + final TransitionInfo.AnimationOptions options; + if (Flags.moveAnimationOptionsToChange()) { + options = info.getAnimationOptions(); + } else { + options = change.getAnimationOptions(); + } + if (options != null) { attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(), cornerRadius); } @@ -725,7 +732,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final boolean isOpeningType = TransitionUtil.isOpeningType(type); final boolean enter = TransitionUtil.isOpeningType(changeMode); final boolean isTask = change.getTaskInfo() != null; - final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + final TransitionInfo.AnimationOptions options; + if (Flags.moveAnimationOptionsToChange()) { + options = change.getAnimationOptions(); + } else { + options = info.getAnimationOptions(); + } final int overrideType = options != null ? options.getType() : ANIM_NONE; final Rect endBounds = TransitionUtil.isClosingType(changeMode) ? mRotator.getEndBoundsInStartRotation(change) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java index ad4f02d13cc6..2047b5a88604 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java @@ -55,6 +55,7 @@ import android.window.TransitionInfo; import com.android.internal.R; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; +import com.android.window.flags.Flags; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.TransitionUtil; @@ -71,7 +72,12 @@ public class TransitionAnimationHelper { final int changeFlags = change.getFlags(); final boolean enter = TransitionUtil.isOpeningType(changeMode); final boolean isTask = change.getTaskInfo() != null; - final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + final TransitionInfo.AnimationOptions options; + if (Flags.moveAnimationOptionsToChange()) { + options = change.getAnimationOptions(); + } else { + options = info.getAnimationOptions(); + } final int overrideType = options != null ? options.getType() : ANIM_NONE; int animAttr = 0; boolean translucent = false; @@ -246,7 +252,7 @@ public class TransitionAnimationHelper { if (!a.getShowBackdrop()) { return defaultColor; } - if (info.getAnimationOptions() != null + if (!Flags.moveAnimationOptionsToChange() && info.getAnimationOptions() != null && info.getAnimationOptions().getBackgroundColor() != 0) { // If available use the background color provided through AnimationOptions return info.getAnimationOptions().getBackgroundColor(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index ea522cdf2509..bd20c1143262 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -32,14 +32,19 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.animation.Animator; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.window.TransitionInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.window.flags.Flags; import com.android.wm.shell.transition.TransitionInfoBuilder; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -56,12 +61,16 @@ import java.util.ArrayList; @RunWith(AndroidJUnit4.class) public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnimationTestBase { + @Rule + public SetFlagsRule mRule = new SetFlagsRule(); + @Before public void setup() { super.setUp(); doNothing().when(mController).onAnimationFinished(any()); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testStartAnimation() { final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) @@ -87,6 +96,7 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim verify(mController).onAnimationFinished(mTransition); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testChangesBehindStartingWindow() { final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) @@ -101,6 +111,7 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim assertEquals(0, animator.getDuration()); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testTransitionTypeDragResize() { final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TASK_FRAGMENT_DRAG_RESIZE, 0) @@ -115,8 +126,9 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim assertEquals(0, animator.getDuration()); } + @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test - public void testInvalidCustomAnimation() { + public void testInvalidCustomAnimation_disableAnimationOptionsPerChange() { final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) .build(); @@ -131,4 +143,22 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim // An invalid custom animation is equivalent to jump-cut. assertEquals(0, animator.getDuration()); } + + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) + @Test + public void testInvalidCustomAnimation_enableAnimationOptionsPerChange() { + final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) + .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) + .build(); + info.getChanges().getFirst().setAnimationOptions(TransitionInfo.AnimationOptions + .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */, + 0 /* backgroundColor */, false /* overrideTaskTransition */)); + final Animator animator = mAnimRunner.createAnimator( + info, mStartTransaction, mFinishTransaction, + () -> mFinishCallback.onTransitionFinished(null /* wct */), + new ArrayList<>()); + + // An invalid custom animation is equivalent to jump-cut. + assertEquals(0, animator.getDuration()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java index 974d69b2ac5d..39d55079ca3a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java @@ -32,6 +32,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import android.animation.Animator; import android.animation.ValueAnimator; import android.graphics.Rect; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -39,9 +42,11 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.window.flags.Flags; import com.android.wm.shell.transition.TransitionInfoBuilder; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,6 +64,9 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation private static final Rect EMBEDDED_LEFT_BOUNDS = new Rect(0, 0, 500, 500); private static final Rect EMBEDDED_RIGHT_BOUNDS = new Rect(500, 0, 1000, 500); + @Rule + public SetFlagsRule mRule = new SetFlagsRule(); + @Before public void setup() { super.setUp(); @@ -66,11 +74,13 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation any()); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testInstantiate() { verify(mShellInit).addInitCallback(any(), any()); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOnInit() { mController.onInit(); @@ -78,6 +88,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verify(mTransitions).addHandler(mController); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testSetAnimScaleSetting() { mController.setAnimScaleSetting(1.0f); @@ -86,6 +97,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verify(mAnimSpec).setAnimScaleSetting(1.0f); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testStartAnimation_containsNonActivityEmbeddingChange() { final TransitionInfo.Change nonEmbeddedOpen = createChange(0 /* flags */); @@ -122,6 +134,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation assertFalse(info2.getChanges().contains(nonEmbeddedClose)); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testStartAnimation_containsOnlyFillTaskActivityEmbeddingChange() { final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) @@ -138,6 +151,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verifyNoMoreInteractions(mFinishCallback); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testStartAnimation_containsActivityEmbeddingSplitChange() { // Change that occupies only part of the Task. @@ -155,6 +169,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verifyNoMoreInteractions(mFinishTransaction); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testStartAnimation_containsChangeEnterActivityEmbeddingSplit() { // Change that is entering ActivityEmbedding split. @@ -171,6 +186,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verifyNoMoreInteractions(mFinishTransaction); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testStartAnimation_containsChangeExitActivityEmbeddingSplit() { // Change that is exiting ActivityEmbedding split. @@ -187,8 +203,9 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verifyNoMoreInteractions(mFinishTransaction); } + @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test - public void testShouldAnimate_containsAnimationOptions() { + public void testShouldAnimate_containsAnimationOptions_disableAnimOptionsPerChange() { final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0) .addChange(createEmbeddedChange(EMBEDDED_RIGHT_BOUNDS, TASK_BOUNDS, TASK_BOUNDS)) .build(); @@ -206,6 +223,28 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation assertFalse(mController.shouldAnimate(info)); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) + @Test + public void testShouldAnimate_containsAnimationOptions_enableAnimOptionsPerChange() { + final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0) + .addChange(createEmbeddedChange(EMBEDDED_RIGHT_BOUNDS, TASK_BOUNDS, TASK_BOUNDS)) + .build(); + final TransitionInfo.Change change = info.getChanges().getFirst(); + + change.setAnimationOptions(TransitionInfo.AnimationOptions + .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */, + 0 /* backgroundColor */, false /* overrideTaskTransition */)); + assertTrue(mController.shouldAnimate(info)); + + change.setAnimationOptions(TransitionInfo.AnimationOptions + .makeSceneTransitionAnimOptions()); + assertFalse(mController.shouldAnimate(info)); + + change.setAnimationOptions(TransitionInfo.AnimationOptions.makeCrossProfileAnimOptions()); + assertFalse(mController.shouldAnimate(info)); + } + + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @UiThreadTest @Test public void testMergeAnimation() { @@ -242,6 +281,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation verify(mFinishCallback).onTransitionFinished(any()); } + @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOnAnimationFinished() { // Should not call finish when there is no transition. diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 28369fa74527..d2b912e9a7e3 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -43,6 +43,7 @@ import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionType; import static android.view.WindowManager.transitTypeToString; import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR; +import static android.window.TransitionInfo.AnimationOptions; import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION; import static android.window.TransitionInfo.FLAG_CONFIG_AT_END; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; @@ -105,6 +106,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.window.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -318,6 +320,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { */ ArrayList<ActivityRecord> mConfigAtEndActivities = null; + @VisibleForTesting Transition(@TransitionType int type, @TransitionFlags int flags, TransitionController controller, BLASTSyncEngine syncEngine) { mType = type; @@ -2668,6 +2671,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return out; } + final AnimationOptions animOptions = calculateAnimationOptionsForActivityTransition(type, + sortedTargets); + if (!Flags.moveAnimationOptionsToChange() && animOptions != null) { + out.setAnimationOptions(animOptions); + } + // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order. final int count = sortedTargets.size(); for (int i = 0; i < count; ++i) { @@ -2757,6 +2766,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255)); } + if (Flags.moveAnimationOptionsToChange() && activityRecord != null + && animOptions != null) { + change.setAnimationOptions(animOptions); + } + if (activityRecord != null) { change.setActivityComponent(activityRecord.mActivityComponent); } @@ -2768,11 +2782,26 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { out.addChange(change); } + return out; + } + /** + * Calculates {@link AnimationOptions} for activity-to-activity transition. + * It returns a valid {@link AnimationOptions} if: + * <ul> + * <li>the top animation target is an Activity</li> + * <li>there's a {@link android.view.Window#setWindowAnimations(int)} and there's only + * {@link WindowState}, {@link WindowToken} and {@link ActivityRecord} target</li> + * </ul> + * Otherwise, it returns {@code null}. + */ + @Nullable + private static AnimationOptions calculateAnimationOptionsForActivityTransition( + @TransitionType int type, @NonNull ArrayList<ChangeInfo> sortedTargets) { TransitionInfo.AnimationOptions animOptions = null; - // Check if the top-most app is an activity (ie. activity->activity). If so, make sure to - // honor its custom transition options. + // Check if the top-most app is an activity (ie. activity->activity). If so, make sure + // to honor its custom transition options. WindowContainer<?> topApp = null; for (int i = 0; i < sortedTargets.size(); i++) { if (isWallpaper(sortedTargets.get(i).mContainer)) continue; @@ -2781,16 +2810,18 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } if (topApp.asActivityRecord() != null) { final ActivityRecord topActivity = topApp.asActivityRecord(); - animOptions = addCustomActivityTransition(topActivity, true/* open */, null); - animOptions = addCustomActivityTransition(topActivity, false/* open */, animOptions); + animOptions = addCustomActivityTransition(topActivity, true/* open */, + null /* animOptions */); + animOptions = addCustomActivityTransition(topActivity, false/* open */, + animOptions); } final WindowManager.LayoutParams animLp = getLayoutParamsForAnimationsStyle(type, sortedTargets); if (animLp != null && animLp.type != TYPE_APPLICATION_STARTING && animLp.windowAnimations != 0) { - // Don't send animation options if no windowAnimations have been set or if the we are - // running an app starting animation, in which case we don't want the app to be able to - // change its animation directly. + // Don't send animation options if no windowAnimations have been set or if the we + // are running an app starting animation, in which case we don't want the app to be + // able to change its animation directly. if (animOptions != null) { animOptions.addOptionsFromLayoutParameters(animLp); } else { @@ -2798,20 +2829,29 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { .makeAnimOptionsFromLayoutParameters(animLp); } } - if (animOptions != null) { - out.setAnimationOptions(animOptions); - } - return out; + return animOptions; } - static TransitionInfo.AnimationOptions addCustomActivityTransition(ActivityRecord topActivity, - boolean open, TransitionInfo.AnimationOptions animOptions) { + /** + * Returns {@link TransitionInfo.AnimationOptions} with custom Activity transition appended if + * {@code topActivity} specifies {@link ActivityRecord#getCustomAnimation(boolean)}, or + * {@code animOptions}, otherwise. + * <p> + * If the passed {@code animOptions} is {@code null}, this method will creates an + * {@link TransitionInfo.AnimationOptions} with custom animation appended + * + * @param open {@code true} to add a custom open animation, and {@false} to add a close one + */ + @Nullable + private static TransitionInfo.AnimationOptions addCustomActivityTransition( + @NonNull ActivityRecord activity, boolean open, + @Nullable TransitionInfo.AnimationOptions animOptions) { final ActivityRecord.CustomAppTransition customAnim = - topActivity.getCustomAnimation(open); + activity.getCustomAnimation(open); if (customAnim != null) { if (animOptions == null) { animOptions = TransitionInfo.AnimationOptions - .makeCommonAnimOptions(topActivity.packageName); + .makeCommonAnimOptions(activity.packageName); } animOptions.addCustomActivityTransition(open, customAnim.mEnterAnim, customAnim.mExitAnim, customAnim.mBackgroundColor); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 698afaa8e8ab..78480f1465d7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -81,7 +81,9 @@ import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import android.util.ArraySet; import android.view.SurfaceControl; @@ -100,7 +102,9 @@ import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; import com.android.internal.graphics.ColorUtils; +import com.android.window.flags.Flags; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -116,6 +120,7 @@ import java.util.function.Function; * Build/Install/Run: * atest WmTests:TransitionTests */ +@EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @SmallTest @Presubmit @RunWith(WindowTestRunner.class) @@ -123,6 +128,8 @@ public class TransitionTests extends WindowTestsBase { final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class); private BLASTSyncEngine mSyncEngine; + @Rule + public SetFlagsRule mRule = new SetFlagsRule(); private Transition createTestTransition(int transitType, TransitionController controller) { final Transition transition = new Transition(transitType, 0 /* flags */, controller, controller.mSyncEngine); -- GitLab