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

Commit 3ab0e1d6 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Use jump-cut for invalid custom animation of embedded activity

App may pass zero resource as custom animation to skip animation.
For the case, return a no-op animation with duration 0, so the
ActivityEmbeddingAnimationRunner#createAnimator can get empty
adapters to perform the expected jump-cut.

This aligns the same behavior as non-embedded default case.

Fix: 308684712
Test: ActivityEmbeddingAnimationRunnerTests#testInvalidCustomAnimation

Change-Id: Ie62fc70a74319840ed197cee73ae9d37fa628971
parent 87e51c10
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -255,8 +255,13 @@ class ActivityEmbeddingAnimationRunner {
        int offsetLayer = TYPE_LAYER_OFFSET;
        final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
        for (TransitionInfo.Change change : openingChanges) {
            final Animation animation =
                    animationProvider.get(info, change, openingWholeScreenBounds);
            if (animation.getDuration() == 0) {
                continue;
            }
            final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
                    info, change, animationProvider, openingWholeScreenBounds);
                    info, change, animation, openingWholeScreenBounds);
            if (isOpening) {
                adapter.overrideLayer(offsetLayer++);
            }
@@ -275,8 +280,13 @@ class ActivityEmbeddingAnimationRunner {
                    adapters.add(snapshotAdapter);
                }
            }
            final Animation animation =
                    animationProvider.get(info, change, closingWholeScreenBounds);
            if (animation.getDuration() == 0) {
                continue;
            }
            final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
                    info, change, animationProvider, closingWholeScreenBounds);
                    info, change, animation, closingWholeScreenBounds);
            if (!isOpening) {
                adapter.overrideLayer(offsetLayer++);
            }
@@ -353,8 +363,7 @@ class ActivityEmbeddingAnimationRunner {
    @NonNull
    private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter(
            @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
            @NonNull AnimationProvider animationProvider, @NonNull Rect wholeAnimationBounds) {
        final Animation animation = animationProvider.get(info, change, wholeAnimationBounds);
            @NonNull Animation animation, @NonNull Rect wholeAnimationBounds) {
        return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
                wholeAnimationBounds, TransitionUtil.getRootFor(change, info));
    }
+25 −10
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static android.app.ActivityOptions.ANIM_CUSTOM;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.view.animation.AlphaAnimation;
@@ -34,8 +36,6 @@ import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.window.TransitionInfo;

import androidx.annotation.NonNull;

import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.util.TransitionUtil;

@@ -201,11 +201,10 @@ class ActivityEmbeddingAnimationSpec {
    Animation loadOpenAnimation(@NonNull TransitionInfo info,
            @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
        final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
        final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
        final Animation customAnimation = loadCustomAnimation(info, isEnter);
        final Animation animation;
        if (options != null && options.getType() == ANIM_CUSTOM) {
            animation = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
                    isEnter ? options.getEnterResId() : options.getExitResId());
        if (customAnimation != null) {
            animation = customAnimation;
        } else if (shouldShowBackdrop(info, change)) {
            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                    ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
@@ -229,11 +228,10 @@ class ActivityEmbeddingAnimationSpec {
    Animation loadCloseAnimation(@NonNull TransitionInfo info,
            @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
        final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
        final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
        final Animation customAnimation = loadCustomAnimation(info, isEnter);
        final Animation animation;
        if (options != null && options.getType() == ANIM_CUSTOM) {
            animation = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
                    isEnter ? options.getEnterResId() : options.getExitResId());
        if (customAnimation != null) {
            animation = customAnimation;
        } else if (shouldShowBackdrop(info, change)) {
            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                    ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
@@ -259,4 +257,21 @@ class ActivityEmbeddingAnimationSpec {
                mTransitionAnimation, false);
        return a != null && a.getShowBackdrop();
    }

    @Nullable
    private Animation loadCustomAnimation(@NonNull TransitionInfo info, boolean isEnter) {
        final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
        if (options == null || options.getType() != ANIM_CUSTOM) {
            return null;
        }
        final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
                isEnter ? options.getEnterResId() : options.getExitResId());
        if (anim != null) {
            return anim;
        }
        // The app may be intentional to use an invalid resource as a no-op animation.
        // ActivityEmbeddingAnimationRunner#createOpenCloseAnimationAdapters will skip the
        // animation with duration 0. Then it will use prepareForJumpCut for empty adapters.
        return new AlphaAnimation(1f, 1f);
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -98,4 +98,21 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
        // The animation should be empty when it is behind starting window.
        assertEquals(0, animator.getDuration());
    }

    @Test
    public void testInvalidCustomAnimation() {
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
                .build();
        info.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());
    }
}