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

Commit 1d90ad8b authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

Merge "Fix ActivityEmbedding animation with new split in different layout" into tm-qpr-dev

parents 53c5aa67 f2296dcb
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
@@ -253,6 +254,10 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
    @NonNull
    private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
            @NonNull RemoteAnimationTarget[] targets) {
        if (shouldUseJumpCutForChangeAnimation(targets)) {
            return new ArrayList<>();
        }

        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
        for (RemoteAnimationTarget target : targets) {
            if (target.mode == MODE_CHANGING) {
@@ -282,4 +287,24 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
        }
        return adapters;
    }

    /**
     * Whether we should use jump cut for the change transition.
     * This normally happens when opening a new secondary with the existing primary using a
     * different split layout. This can be complicated, like from horizontal to vertical split with
     * new split pairs.
     * Uses a jump cut animation to simplify.
     */
    private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) {
        boolean hasOpeningWindow = false;
        boolean hasClosingWindow = false;
        for (RemoteAnimationTarget target : targets) {
            if (target.hasAnimatingParent) {
                continue;
            }
            hasOpeningWindow |= target.mode == MODE_OPENING;
            hasClosingWindow |= target.mode == MODE_CLOSING;
        }
        return hasOpeningWindow && hasClosingWindow;
    }
}
+99 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.activityembedding;

import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;

@@ -112,15 +113,20 @@ class ActivityEmbeddingAnimationRunner {
            @NonNull List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks) {
        final List<ActivityEmbeddingAnimationAdapter> adapters = createAnimationAdapters(info,
                startTransaction);
        addEdgeExtensionIfNeeded(startTransaction, finishTransaction, postStartTransactionCallbacks,
                adapters);
        addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters);
        final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        long duration = 0;
        if (adapters.isEmpty()) {
            // Jump cut
            // No need to modify the animator, but to update the startTransaction with the changes'
            // ending states.
            prepareForJumpCut(info, startTransaction);
        } else {
            addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
                    postStartTransactionCallbacks, adapters);
            addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters);
            for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
                duration = Math.max(duration, adapter.getDurationHint());
            }
        final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(duration);
            animator.addUpdateListener((anim) -> {
                // Update all adapters in the same transaction.
                final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -129,6 +135,8 @@ class ActivityEmbeddingAnimationRunner {
                }
                t.apply();
            });
        }
        animator.setDuration(duration);
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {}
@@ -292,6 +300,10 @@ class ActivityEmbeddingAnimationRunner {
    @NonNull
    private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
        if (shouldUseJumpCutForChangeTransition(info)) {
            return new ArrayList<>();
        }

        final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
        final Set<TransitionInfo.Change> handledChanges = new ArraySet<>();

@@ -374,9 +386,11 @@ class ActivityEmbeddingAnimationRunner {
            }

            final Animation animation;
            if (change.getParent() != null
                    && handledChanges.contains(info.getChange(change.getParent()))) {
                // No-op if it will be covered by the changing parent window.
            if ((change.getParent() != null
                    && handledChanges.contains(info.getChange(change.getParent())))
                    || change.getMode() == TRANSIT_CHANGE) {
                // No-op if it will be covered by the changing parent window, or it is a changing
                // window without bounds change.
                animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
            } else if (Transitions.isClosingType(change.getMode())) {
                animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
@@ -421,6 +435,74 @@ class ActivityEmbeddingAnimationRunner {
                animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
    }

    /**
     * Whether we should use jump cut for the change transition.
     * This normally happens when opening a new secondary with the existing primary using a
     * different split layout. This can be complicated, like from horizontal to vertical split with
     * new split pairs.
     * Uses a jump cut animation to simplify.
     */
    private boolean shouldUseJumpCutForChangeTransition(@NonNull TransitionInfo info) {
        // There can be reparenting of changing Activity to new open TaskFragment, so we need to
        // exclude both in the first iteration.
        final List<TransitionInfo.Change> changingChanges = new ArrayList<>();
        for (TransitionInfo.Change change : info.getChanges()) {
            if (change.getMode() != TRANSIT_CHANGE
                    || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
                continue;
            }
            changingChanges.add(change);
            final WindowContainerToken parentToken = change.getParent();
            if (parentToken != null) {
                // When the parent window is also included in the transition as an opening window,
                // we would like to animate the parent window instead.
                final TransitionInfo.Change parentChange = info.getChange(parentToken);
                if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
                    changingChanges.add(parentChange);
                }
            }
        }
        if (changingChanges.isEmpty()) {
            // No changing target found.
            return true;
        }

        // Check if the transition contains both opening and closing windows.
        boolean hasOpeningWindow = false;
        boolean hasClosingWindow = false;
        for (TransitionInfo.Change change : info.getChanges()) {
            if (changingChanges.contains(change)) {
                continue;
            }
            if (change.getParent() != null
                    && changingChanges.contains(info.getChange(change.getParent()))) {
                // No-op if it will be covered by the changing parent window.
                continue;
            }
            hasOpeningWindow |= Transitions.isOpeningType(change.getMode());
            hasClosingWindow |= Transitions.isClosingType(change.getMode());
        }
        return hasOpeningWindow && hasClosingWindow;
    }

    /** Updates the changes to end states in {@code startTransaction} for jump cut animation. */
    private void prepareForJumpCut(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction) {
        for (TransitionInfo.Change change : info.getChanges()) {
            final SurfaceControl leash = change.getLeash();
            startTransaction.setPosition(leash,
                    change.getEndRelOffset().x, change.getEndRelOffset().y);
            startTransaction.setWindowCrop(leash,
                    change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
            if (change.getMode() == TRANSIT_CLOSE) {
                startTransaction.hide(leash);
            } else {
                startTransaction.show(leash);
                startTransaction.setAlpha(leash, 1f);
            }
        }
    }

    /** To provide an {@link Animation} based on the transition infos. */
    private interface AnimationProvider {
        Animation get(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,