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

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

Merge "Fix ActivityEmbedding Shell transition animation with reparent" into tm-qpr-dev

parents 9170b62c c72e0d11
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -170,9 +170,11 @@ class ActivityEmbeddingAnimationAdapter {
        void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
            super.onAnimationEnd(t);
            // Remove the screenshot leash after animation is finished.
            if (mLeash.isValid()) {
                t.remove(mLeash);
            }
        }
    }

    /**
     * Should be used for the animation of the {@link TransitionInfo.Change} that has size change.
+75 −32
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.animation.Animation;
@@ -39,6 +40,7 @@ import com.android.wm.shell.transition.Transitions;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;

/** To run the ActivityEmbedding animations. */
@@ -214,25 +216,47 @@ class ActivityEmbeddingAnimationRunner {
    private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
        final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
        final Set<TransitionInfo.Change> handledChanges = new ArraySet<>();

        // For the first iteration, we prepare the animation for the change type windows. This is
        // needed because there may be window that is reparented while resizing. In such case, we
        // will do the following:
        // 1. Capture a screenshot from the Activity surface.
        // 2. Attach the screenshot surface to the top of TaskFragment (Activity's parent) surface.
        // 3. Animate the TaskFragment using Activity Change info (start/end bounds).
        // This is because the TaskFragment surface/change won't contain the Activity's before its
        // reparent.
        for (TransitionInfo.Change change : info.getChanges()) {
            if (change.getMode() == TRANSIT_CHANGE
                    && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
            if (change.getMode() != TRANSIT_CHANGE
                    || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
                continue;
            }

            // This is the window with bounds change.
            handledChanges.add(change);
            final WindowContainerToken parentToken = change.getParent();
                final Rect parentBounds;
            TransitionInfo.Change boundsAnimationChange = change;
            if (parentToken != null) {
                    TransitionInfo.Change parentChange = info.getChange(parentToken);
                    parentBounds = parentChange != null
                            ? parentChange.getEndAbsBounds()
                            : change.getEndAbsBounds();
                } else {
                    parentBounds = change.getEndAbsBounds();
                // 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())) {
                    // We won't create a separate animation for the parent, but to animate the
                    // parent for the child resizing.
                    handledChanges.add(parentChange);
                    boundsAnimationChange = parentChange;
                }
                final Animation[] animations =
                        mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
                // Adapter for the starting screenshot leash.
                final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction);
            }

            final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
                    boundsAnimationChange.getEndAbsBounds());

            // Create a screenshot based on change, but attach it to the top of the
            // boundsAnimationChange.
            final SurfaceControl screenshotLeash = getOrCreateScreenshot(change,
                    boundsAnimationChange, startTransaction);
            if (screenshotLeash != null) {
                // Adapter for the starting screenshot leash.
                // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
                adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
                        animations[0], change, screenshotLeash));
@@ -241,11 +265,16 @@ class ActivityEmbeddingAnimationRunner {
            }
            // Adapter for the ending bounds changed leash.
            adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
                        animations[1], change));
                    animations[1], boundsAnimationChange));
        }

        // Handle the other windows that don't have bounds change in the same transition.
        for (TransitionInfo.Change change : info.getChanges()) {
            if (handledChanges.contains(change)) {
                // Skip windows that we have already handled in the previous iteration.
                continue;
            }

            // These are the other windows that don't have bounds change in the same transition.
            final Animation animation;
            if (!TransitionInfo.isIndependent(change, info)) {
                // No-op if it will be covered by the changing parent window.
@@ -260,13 +289,27 @@ class ActivityEmbeddingAnimationRunner {
        return adapters;
    }

    /** Takes a screenshot of the given {@link TransitionInfo.Change} surface. */
    /**
     * 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
     * animate later.
     */
    @Nullable
    private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change,
            @NonNull SurfaceControl.Transaction startTransaction) {
        final Rect cropBounds = new Rect(change.getStartAbsBounds());
    private SurfaceControl getOrCreateScreenshot(@NonNull TransitionInfo.Change screenshotChange,
            @NonNull TransitionInfo.Change animationChange,
            @NonNull SurfaceControl.Transaction t) {
        final SurfaceControl screenshotLeash = screenshotChange.getSnapshot();
        if (screenshotLeash != null) {
            // If WM Core has already taken a screenshot, make sure it is reparented to the
            // animation leash.
            t.reparent(screenshotLeash, animationChange.getLeash());
            return screenshotLeash;
        }

        // If WM Core hasn't taken a screenshot, take a screenshot now.
        final Rect cropBounds = new Rect(screenshotChange.getStartAbsBounds());
        cropBounds.offsetTo(0, 0);
        return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds,
                Integer.MAX_VALUE);
        return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(),
                animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
    }
}
+24 −5
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import java.util.function.Consumer;
public class ScreenshotUtils {

    /**
     * Take a screenshot of the specified SurfaceControl.
     * Takes a screenshot of the specified SurfaceControl.
     *
     * @param sc the SurfaceControl to take a screenshot of
     * @param crop the crop to use when capturing the screenshot
@@ -49,11 +49,14 @@ public class ScreenshotUtils {
        SurfaceControl mScreenshot = null;
        SurfaceControl.Transaction mTransaction;
        SurfaceControl mSurfaceControl;
        SurfaceControl mParentSurfaceControl;
        int mLayer;

        BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) {
        BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, SurfaceControl parentSc,
                int layer) {
            mTransaction = t;
            mSurfaceControl = sc;
            mParentSurfaceControl = parentSc;
            mLayer = layer;
        }

@@ -72,7 +75,7 @@ public class ScreenshotUtils {

            mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
            mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
            mTransaction.reparent(mScreenshot, mSurfaceControl);
            mTransaction.reparent(mScreenshot, mParentSurfaceControl);
            mTransaction.setLayer(mScreenshot, mLayer);
            mTransaction.show(mScreenshot);
            mTransaction.apply();
@@ -80,7 +83,7 @@ public class ScreenshotUtils {
    }

    /**
     * Take a screenshot of the specified SurfaceControl.
     * Takes a screenshot of the specified SurfaceControl.
     *
     * @param t the transaction used to set changes on the resulting screenshot.
     * @param sc the SurfaceControl to take a screenshot of
@@ -91,7 +94,23 @@ public class ScreenshotUtils {
     */
    public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
            Rect crop, int layer) {
        BufferConsumer consumer = new BufferConsumer(t, sc, layer);
        return takeScreenshot(t, sc, sc /* parentSc */, crop, layer);
    }

    /**
     * Takes a screenshot of the specified SurfaceControl.
     *
     * @param t the transaction used to set changes on the resulting screenshot.
     * @param sc the SurfaceControl to take a screenshot of
     * @param parentSc  the SurfaceControl to attach the screenshot to.
     * @param crop the crop to use when capturing the screenshot
     * @param layer the layer to place the screenshot
     *
     * @return A SurfaceControl where the screenshot will be attached, or null if failed.
     */
    public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
            SurfaceControl parentSc, Rect crop, int layer) {
        BufferConsumer consumer = new BufferConsumer(t, sc, parentSc, layer);
        captureLayer(sc, crop, consumer);
        return consumer.mScreenshot;
    }
+11 −2
Original line number Diff line number Diff line
@@ -1522,9 +1522,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        this.task = newTask;

        if (shouldStartChangeTransition(newParent, oldParent)) {
            // Animate change transition on TaskFragment level to get the correct window crop.
            if (mTransitionController.isShellTransitionsEnabled()) {
                // For Shell transition, call #initializeChangeTransition directly to take the
                // screenshot at the Activity level. And Shell will be in charge of handling the
                // surface reparent and crop.
                initializeChangeTransition(getBounds());
            } else {
                // For legacy app transition, we want to take a screenshot of the Activity surface,
                // but animate the change transition on TaskFragment level to get the correct window
                // crop.
                newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
            }
        }

        super.onParentChanged(newParent, oldParent);