Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64f38830 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add animation override support to AE" into main

parents 9fc8c08f d2c6e463
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -663,6 +663,7 @@ public final class TransitionInfo implements Parcelable {
        private final Rect mStartAbsBounds = new Rect();
        private final Rect mEndAbsBounds = new Rect();
        private final Point mEndRelOffset = new Point();
        private final Point mEndParentSize = new Point();
        private ActivityManager.RunningTaskInfo mTaskInfo = null;
        private boolean mAllowEnterPip;
        private int mStartDisplayId = INVALID_DISPLAY;
@@ -697,6 +698,7 @@ public final class TransitionInfo implements Parcelable {
            mStartAbsBounds.readFromParcel(in);
            mEndAbsBounds.readFromParcel(in);
            mEndRelOffset.readFromParcel(in);
            mEndParentSize.readFromParcel(in);
            mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
            mAllowEnterPip = in.readBoolean();
            mStartDisplayId = in.readInt();
@@ -721,6 +723,7 @@ public final class TransitionInfo implements Parcelable {
            out.mStartAbsBounds.set(mStartAbsBounds);
            out.mEndAbsBounds.set(mEndAbsBounds);
            out.mEndRelOffset.set(mEndRelOffset);
            out.mEndParentSize.set(mEndParentSize);
            out.mTaskInfo = mTaskInfo;
            out.mAllowEnterPip = mAllowEnterPip;
            out.mStartDisplayId = mStartDisplayId;
@@ -780,6 +783,13 @@ public final class TransitionInfo implements Parcelable {
            mEndRelOffset.set(left, top);
        }

        /**
         * Sets the size of its parent container after the change.
         */
        public void setEndParentSize(int width, int height) {
            mEndParentSize.set(width, height);
        }

        /**
         * Sets the taskinfo of this container if this is a task. WARNING: this takes the
         * reference, so don't modify it afterwards.
@@ -916,6 +926,14 @@ public final class TransitionInfo implements Parcelable {
            return mEndRelOffset;
        }

        /**
         * Returns the size of parent container after the change.
         */
        @NonNull
        public Point getEndParentSize() {
            return mEndParentSize;
        }

        /** @return the leash or surface to animate for this container */
        @NonNull
        public SurfaceControl getLeash() {
@@ -1003,6 +1021,7 @@ public final class TransitionInfo implements Parcelable {
            mStartAbsBounds.writeToParcel(dest, flags);
            mEndAbsBounds.writeToParcel(dest, flags);
            mEndRelOffset.writeToParcel(dest, flags);
            mEndParentSize.writeToParcel(dest, flags);
            dest.writeTypedObject(mTaskInfo, flags);
            dest.writeBoolean(mAllowEnterPip);
            dest.writeInt(mStartDisplayId);
@@ -1055,6 +1074,9 @@ public final class TransitionInfo implements Parcelable {
            if (mEndRelOffset.x != 0 || mEndRelOffset.y != 0) {
                sb.append(" eo="); sb.append(mEndRelOffset);
            }
            if (!mEndParentSize.equals(0, 0)) {
                sb.append(" epz=").append(mEndParentSize);
            }
            sb.append(" d=");
            if (mStartDisplayId != mEndDisplayId) {
                sb.append(mStartDisplayId).append("->");
+59 −14
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_TASK_FRAGMENT_
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
@@ -45,6 +46,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.activityembedding.ActivityEmbeddingAnimationAdapter.SnapshotAdapter;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.shared.TransitionUtil;
@@ -398,7 +400,15 @@ class ActivityEmbeddingAnimationRunner {
        // This is because the TaskFragment surface/change won't contain the Activity's before its
        // reparent.
        Animation changeAnimation = null;
        Rect parentBounds = new Rect();
        final Rect parentBounds = new Rect();
        // We use a single boolean value to record the backdrop override because the override used
        // for overlay and we restrict to single overlay animation. We should fix the assumption
        // if we allow multiple overlay transitions.
        // The backdrop logic is mainly for animations of split animations. The backdrop should be
        // disabled if there is any open/close target in the same transition as the change target.
        // However, the overlay change animation usually contains one change target, and shows
        // backdrop unexpectedly.
        Boolean overrideShowBackdrop = null;
        for (TransitionInfo.Change change : info.getChanges()) {
            if (change.getMode() != TRANSIT_CHANGE
                    || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
@@ -421,17 +431,17 @@ class ActivityEmbeddingAnimationRunner {
                }
            }

            // The TaskFragment may be enter/exit split, so we take the union of both as the parent
            // size.
            parentBounds.union(boundsAnimationChange.getStartAbsBounds());
            parentBounds.union(boundsAnimationChange.getEndAbsBounds());
            if (boundsAnimationChange != change) {
                // Union the change starting bounds in case the activity is resized and reparented
                // to a TaskFragment. In that case, the TaskFragment may not cover the activity's
                // starting bounds.
                parentBounds.union(change.getStartAbsBounds());
            final TransitionInfo.AnimationOptions options = boundsAnimationChange
                    .getAnimationOptions();
            if (options != null) {
                final Animation overrideAnimation = mAnimationSpec.loadCustomAnimationFromOptions(
                        options, TRANSIT_CHANGE);
                if (overrideAnimation != null) {
                    overrideShowBackdrop = overrideAnimation.getShowBackdrop();
                }
            }

            calculateParentBounds(change, boundsAnimationChange, parentBounds);
            // There are two animations in the array. The first one is for the start leash
            // (snapshot), and the second one is for the end leash (TaskFragment).
            final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
@@ -466,7 +476,7 @@ class ActivityEmbeddingAnimationRunner {

        // If there is no corresponding open/close window with the change, we should show background
        // color to cover the empty part of the screen.
        boolean shouldShouldBackgroundColor = true;
        boolean shouldShowBackgroundColor = true;
        // Handle the other windows that don't have bounds change in the same transition.
        for (TransitionInfo.Change change : info.getChanges()) {
            if (handledChanges.contains(change)) {
@@ -483,16 +493,18 @@ class ActivityEmbeddingAnimationRunner {
                animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
            } else if (TransitionUtil.isClosingType(change.getMode())) {
                animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
                shouldShouldBackgroundColor = false;
                shouldShowBackgroundColor = false;
            } else {
                animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds);
                shouldShouldBackgroundColor = false;
                shouldShowBackgroundColor = false;
            }
            adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change,
                    TransitionUtil.getRootFor(change, info)));
        }

        if (shouldShouldBackgroundColor && changeAnimation != null) {
        shouldShowBackgroundColor = overrideShowBackdrop != null
                ? overrideShowBackdrop : shouldShowBackgroundColor;
        if (shouldShowBackgroundColor && changeAnimation != null) {
            // Change animation may leave part of the screen empty. Show background color to cover
            // that.
            changeAnimation.setShowBackdrop(true);
@@ -501,6 +513,39 @@ class ActivityEmbeddingAnimationRunner {
        return adapters;
    }

    /**
     * Calculates parent bounds of the animation target by {@code change}.
     */
    @VisibleForTesting
    static void calculateParentBounds(@NonNull TransitionInfo.Change change,
              @NonNull TransitionInfo.Change boundsAnimationChange, @NonNull Rect outParentBounds) {
        if (Flags.activityEmbeddingOverlayPresentationFlag()) {
            final Point endParentSize = change.getEndParentSize();
            if (endParentSize.equals(0, 0)) {
                return;
            }
            final Point endRelPosition = change.getEndRelOffset();
            final Point endAbsPosition = new Point(change.getEndAbsBounds().left,
                    change.getEndAbsBounds().top);
            final Point parentEndAbsPosition = new Point(endAbsPosition.x - endRelPosition.x,
                    endAbsPosition.y - endRelPosition.y);
            outParentBounds.set(parentEndAbsPosition.x, parentEndAbsPosition.y,
                    parentEndAbsPosition.x + endParentSize.x,
                    parentEndAbsPosition.y + endParentSize.y);
        } else {
            // The TaskFragment may be enter/exit split, so we take the union of both as
            // the parent size.
            outParentBounds.union(boundsAnimationChange.getStartAbsBounds());
            outParentBounds.union(boundsAnimationChange.getEndAbsBounds());
            if (boundsAnimationChange != change) {
                // Union the change starting bounds in case the activity is resized and
                // reparented to a TaskFragment. In that case, the TaskFragment may not cover
                // the activity's starting bounds.
                outParentBounds.union(change.getStartAbsBounds());
            }
        }
    }

    /**
     * Takes a screenshot of the given {@code screenshotChange} surface if WM Core hasn't taken one.
     * The screenshot leash should be attached to the {@code animationChange} surface which we will
+30 −4
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.wm.shell.activityembedding;


import static android.app.ActivityOptions.ANIM_CUSTOM;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID;

import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
@@ -27,6 +29,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -203,7 +207,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, change, isEnter);
        final Animation customAnimation = loadCustomAnimation(info, change);
        final Animation animation;
        if (customAnimation != null) {
            animation = customAnimation;
@@ -230,7 +234,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, change, isEnter);
        final Animation customAnimation = loadCustomAnimation(info, change);
        final Animation animation;
        if (customAnimation != null) {
            animation = customAnimation;
@@ -263,18 +267,40 @@ class ActivityEmbeddingAnimationSpec {

    @Nullable
    private Animation loadCustomAnimation(@NonNull TransitionInfo info,
            @NonNull TransitionInfo.Change change, boolean isEnter) {
            @NonNull TransitionInfo.Change change) {
        final TransitionInfo.AnimationOptions options;
        if (Flags.moveAnimationOptionsToChange()) {
            options = change.getAnimationOptions();
        } else {
            options = info.getAnimationOptions();
        }
        return loadCustomAnimationFromOptions(options, change.getMode());
    }

    @Nullable
    Animation loadCustomAnimationFromOptions(@Nullable TransitionInfo.AnimationOptions options,
             @WindowManager.TransitionType int mode) {
        if (options == null || options.getType() != ANIM_CUSTOM) {
            return null;
        }
        final int resId;
        if (TransitionUtil.isOpeningType(mode)) {
            resId = options.getEnterResId();
        } else if (TransitionUtil.isClosingType(mode)) {
            resId = options.getExitResId();
        } else if (mode == TRANSIT_CHANGE) {
            resId = options.getChangeResId();
        } else {
            Log.w(TAG, "Unknown transit type:" + mode);
            resId = DEFAULT_ANIMATION_RESOURCES_ID;
        }
        // Use the default animation if the resources ID is not specified.
        if (resId == DEFAULT_ANIMATION_RESOURCES_ID) {
            return null;
        }

        final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
                isEnter ? options.getEnterResId() : options.getExitResId());
                resId);
        if (anim != null) {
            return anim;
        }
+131 −2
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;

import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.calculateParentBounds;
import static com.android.wm.shell.transition.Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -32,6 +34,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import android.animation.Animator;
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -130,7 +135,7 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
    @Test
    public void testInvalidCustomAnimation_disableAnimationOptionsPerChange() {
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
                .build();
        info.setAnimationOptions(TransitionInfo.AnimationOptions
                .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
@@ -148,7 +153,7 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
    @Test
    public void testInvalidCustomAnimation_enableAnimationOptionsPerChange() {
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
                .build();
        info.getChanges().getFirst().setAnimationOptions(TransitionInfo.AnimationOptions
                .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
@@ -161,4 +166,128 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
        // An invalid custom animation is equivalent to jump-cut.
        assertEquals(0, animator.getDuration());
    }

    @DisableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
    @Test
    public void testCalculateParentBounds_flagDisabled() {
        final Rect parentBounds = new Rect(0, 0, 2000, 2000);
        final Rect primaryBounds = new Rect();
        final Rect secondaryBounds = new Rect();
        parentBounds.splitVertically(primaryBounds, secondaryBounds);

        final TransitionInfo.Change change = createChange(0 /* flags */);
        change.setStartAbsBounds(secondaryBounds);

        final TransitionInfo.Change boundsAnimationChange = createChange(0 /* flags */);
        boundsAnimationChange.setStartAbsBounds(primaryBounds);
        boundsAnimationChange.setEndAbsBounds(primaryBounds);
        final Rect actualParentBounds = new Rect();

        calculateParentBounds(change, boundsAnimationChange, actualParentBounds);

        assertEquals(parentBounds, actualParentBounds);

        actualParentBounds.setEmpty();

        boundsAnimationChange.setStartAbsBounds(secondaryBounds);
        boundsAnimationChange.setEndAbsBounds(primaryBounds);

        calculateParentBounds(boundsAnimationChange, boundsAnimationChange, actualParentBounds);

        assertEquals(parentBounds, actualParentBounds);
    }

    // TODO(b/243518738): Rewrite with TestParameter
    @EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
    @Test
    public void testCalculateParentBounds_flagEnabled() {
        TransitionInfo.Change change;
        final TransitionInfo.Change stubChange = createChange(0 /* flags */);
        final Rect actualParentBounds = new Rect();
        Rect parentBounds = new Rect(0, 0, 2000, 2000);
        Rect endAbsBounds = new Rect(0, 0, 2000, 2000);
        change = prepareChangeForParentBoundsCalculationTest(
                new Point(0, 0) /* endRelOffset */,
                endAbsBounds,
                new Point() /* endParentSize */
        );

        calculateParentBounds(change, stubChange, actualParentBounds);

        assertTrue("Parent bounds must be empty because end parent size is not set.",
                actualParentBounds.isEmpty());

        String testString = "Parent start with (0, 0)";
        change = prepareChangeForParentBoundsCalculationTest(
                new Point(endAbsBounds.left - parentBounds.left,
                        endAbsBounds.top - parentBounds.top),
                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));

        calculateParentBounds(change, stubChange, actualParentBounds);

        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
                actualParentBounds);

        testString = "Container not start with (0, 0)";
        parentBounds = new Rect(0, 0, 2000, 2000);
        endAbsBounds = new Rect(1000, 500, 2000, 1500);
        change = prepareChangeForParentBoundsCalculationTest(
                new Point(endAbsBounds.left - parentBounds.left,
                        endAbsBounds.top - parentBounds.top),
                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));

        calculateParentBounds(change, stubChange, actualParentBounds);

        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
                actualParentBounds);

        testString = "Parent container on the right";
        parentBounds = new Rect(1000, 0, 2000, 2000);
        endAbsBounds = new Rect(1000, 500, 1500, 1500);
        change = prepareChangeForParentBoundsCalculationTest(
                new Point(endAbsBounds.left - parentBounds.left,
                        endAbsBounds.top - parentBounds.top),
                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));

        calculateParentBounds(change, stubChange, actualParentBounds);

        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
                actualParentBounds);

        testString = "Parent container on the bottom";
        parentBounds = new Rect(0, 1000, 2000, 2000);
        endAbsBounds = new Rect(500, 1500, 1500, 2000);
        change = prepareChangeForParentBoundsCalculationTest(
                new Point(endAbsBounds.left - parentBounds.left,
                        endAbsBounds.top - parentBounds.top),
                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));

        calculateParentBounds(change, stubChange, actualParentBounds);

        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
                actualParentBounds);

        testString = "Parent container in the middle";
        parentBounds = new Rect(500, 500, 1500, 1500);
        endAbsBounds = new Rect(1000, 500, 1500, 1000);
        change = prepareChangeForParentBoundsCalculationTest(
                new Point(endAbsBounds.left - parentBounds.left,
                        endAbsBounds.top - parentBounds.top),
                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));

        calculateParentBounds(change, stubChange, actualParentBounds);

        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
                actualParentBounds);
    }

    @NonNull
    private static TransitionInfo.Change prepareChangeForParentBoundsCalculationTest(
            @NonNull Point endRelOffset, @NonNull Rect endAbsBounds, @NonNull Point endParentSize) {
        final TransitionInfo.Change change = createChange(0 /* flags */);
        change.setEndRelOffset(endRelOffset.x, endRelOffset.y);
        change.setEndAbsBounds(endAbsBounds);
        change.setEndParentSize(endParentSize.x, endParentSize.y);
        return change;
    }
}
+19 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.activityembedding;

import static android.view.WindowManager.TRANSIT_NONE;
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;

@@ -31,6 +32,7 @@ import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;

@@ -82,11 +84,27 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase {
        spyOn(mFinishCallback);
    }

    /** Creates a mock {@link TransitionInfo.Change}. */
    /**
     * Creates a mock {@link TransitionInfo.Change}.
     *
     * @param flags the {@link TransitionInfo.ChangeFlags} of the change
     */
    static TransitionInfo.Change createChange(@TransitionInfo.ChangeFlags int flags) {
        return createChange(flags, TRANSIT_NONE);
    }

    /**
     * Creates a mock {@link TransitionInfo.Change}.
     *
     * @param flags the {@link TransitionInfo.ChangeFlags} of the change
     * @param mode the transition mode of the change
     */
    static TransitionInfo.Change createChange(@TransitionInfo.ChangeFlags int flags,
            @WindowManager.TransitionType int mode) {
        TransitionInfo.Change c = new TransitionInfo.Change(mock(WindowContainerToken.class),
                mock(SurfaceControl.class));
        c.setFlags(flags);
        c.setMode(mode);
        return c;
    }

Loading