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

Commit 9baf3ce9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add edge extension for ActivityEmbedding Shell transition animation" into tm-qpr-dev

parents a50b5fcb fcc07472
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -130,6 +130,10 @@ class ActivityEmbeddingAnimationAdapter {
        if (!cropRect.intersect(mWholeAnimationBounds)) {
            // Hide the surface when it is outside of the animation area.
            t.setAlpha(mLeash, 0);
        } else if (mAnimation.hasExtension()) {
            // Allow the surface to be shown in its original bounds in case we want to use edge
            // extensions.
            cropRect.union(mChange.getEndAbsBounds());
        }

        // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+85 −33
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;

import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;

import android.animation.Animator;
@@ -45,6 +46,7 @@ import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

/** To run the ActivityEmbedding animations. */
class ActivityEmbeddingAnimationRunner {
@@ -65,11 +67,32 @@ class ActivityEmbeddingAnimationRunner {
    void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction) {
        // There may be some surface change that we want to apply after the start transaction is
        // applied to make sure the surface is ready.
        final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks =
                new ArrayList<>();
        final Animator animator = createAnimator(info, startTransaction, finishTransaction,
                () -> mController.onAnimationFinished(transition));
                () -> mController.onAnimationFinished(transition), postStartTransactionCallbacks);

        // Start the animation.
        if (!postStartTransactionCallbacks.isEmpty()) {
            // postStartTransactionCallbacks require that the start transaction is already
            // applied to run otherwise they may result in flickers and UI inconsistencies.
            startTransaction.apply(true /* sync */);

            // Run tasks that require startTransaction to already be applied
            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
            for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
                    postStartTransactionCallbacks) {
                postStartTransactionCallback.accept(t);
            }
            t.apply();
            animator.start();
        } else {
            startTransaction.apply();
            animator.start();
        }
    }

    /**
     * Sets transition animation scale settings value.
@@ -85,9 +108,13 @@ class ActivityEmbeddingAnimationRunner {
    Animator createAnimator(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Runnable animationFinishCallback) {
        final List<ActivityEmbeddingAnimationAdapter> adapters =
                createAnimationAdapters(info, startTransaction, finishTransaction);
            @NonNull Runnable animationFinishCallback,
            @NonNull List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks) {
        final List<ActivityEmbeddingAnimationAdapter> adapters = createAnimationAdapters(info,
                startTransaction);
        addEdgeExtensionIfNeeded(startTransaction, finishTransaction, postStartTransactionCallbacks,
                adapters);
        addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters);
        long duration = 0;
        for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
            duration = Math.max(duration, adapter.getDurationHint());
@@ -131,8 +158,7 @@ class ActivityEmbeddingAnimationRunner {
     */
    @NonNull
    private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters(
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction) {
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
        boolean isChangeTransition = false;
        for (TransitionInfo.Change change : info.getChanges()) {
            if (change.hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)) {
@@ -148,25 +174,23 @@ class ActivityEmbeddingAnimationRunner {
            return createChangeAnimationAdapters(info, startTransaction);
        }
        if (Transitions.isClosingType(info.getType())) {
            return createCloseAnimationAdapters(info, startTransaction, finishTransaction);
            return createCloseAnimationAdapters(info);
        }
        return createOpenAnimationAdapters(info, startTransaction, finishTransaction);
        return createOpenAnimationAdapters(info);
    }

    @NonNull
    private List<ActivityEmbeddingAnimationAdapter> createOpenAnimationAdapters(
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction) {
        return createOpenCloseAnimationAdapters(info, startTransaction, finishTransaction,
                true /* isOpening */, mAnimationSpec::loadOpenAnimation);
            @NonNull TransitionInfo info) {
        return createOpenCloseAnimationAdapters(info, true /* isOpening */,
                mAnimationSpec::loadOpenAnimation);
    }

    @NonNull
    private List<ActivityEmbeddingAnimationAdapter> createCloseAnimationAdapters(
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction) {
        return createOpenCloseAnimationAdapters(info, startTransaction, finishTransaction,
                false /* isOpening */, mAnimationSpec::loadCloseAnimation);
            @NonNull TransitionInfo info) {
        return createOpenCloseAnimationAdapters(info, false /* isOpening */,
                mAnimationSpec::loadCloseAnimation);
    }

    /**
@@ -175,8 +199,7 @@ class ActivityEmbeddingAnimationRunner {
     */
    @NonNull
    private List<ActivityEmbeddingAnimationAdapter> createOpenCloseAnimationAdapters(
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction, boolean isOpening,
            @NonNull TransitionInfo info, boolean isOpening,
            @NonNull AnimationProvider animationProvider) {
        // We need to know if the change window is only a partial of the whole animation screen.
        // If so, we will need to adjust it to make the whole animation screen looks like one.
@@ -200,8 +223,7 @@ class ActivityEmbeddingAnimationRunner {
        final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
        for (TransitionInfo.Change change : openingChanges) {
            final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
                    info, change, startTransaction, finishTransaction, animationProvider,
                    openingWholeScreenBounds);
                    info, change, animationProvider, openingWholeScreenBounds);
            if (isOpening) {
                adapter.overrideLayer(offsetLayer++);
            }
@@ -209,8 +231,7 @@ class ActivityEmbeddingAnimationRunner {
        }
        for (TransitionInfo.Change change : closingChanges) {
            final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
                    info, change, startTransaction, finishTransaction, animationProvider,
                    closingWholeScreenBounds);
                    info, change, animationProvider, closingWholeScreenBounds);
            if (!isOpening) {
                adapter.overrideLayer(offsetLayer++);
            }
@@ -219,20 +240,51 @@ class ActivityEmbeddingAnimationRunner {
        return adapters;
    }

    @NonNull
    private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter(
            @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
    /** Adds edge extension to the surfaces that have such an animation property. */
    private void addEdgeExtensionIfNeeded(@NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks,
            @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
        for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
            final Animation animation = adapter.mAnimation;
            if (!animation.hasExtension()) {
                continue;
            }
            final TransitionInfo.Change change = adapter.mChange;
            if (Transitions.isOpeningType(adapter.mChange.getMode())) {
                // Need to screenshot after startTransaction is applied otherwise activity
                // may not be visible or ready yet.
                postStartTransactionCallbacks.add(
                        t -> edgeExtendWindow(change, animation, t, finishTransaction));
            } else {
                // Can screenshot now (before startTransaction is applied)
                edgeExtendWindow(change, animation, startTransaction, finishTransaction);
            }
        }
    }

    /** Adds background color to the transition if any animation has such a property. */
    private void addBackgroundColorIfNeeded(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull AnimationProvider animationProvider, @NonNull Rect wholeAnimationBounds) {
        final Animation animation = animationProvider.get(info, change, wholeAnimationBounds);
        // We may want to show a background color for open/close transition.
        final int backgroundColor = getTransitionBackgroundColorIfSet(info, change, animation,
                0 /* defaultColor */);
            @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
        for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
            final int backgroundColor = getTransitionBackgroundColorIfSet(info, adapter.mChange,
                    adapter.mAnimation, 0 /* defaultColor */);
            if (backgroundColor != 0) {
                // We only need to show one color.
                addBackgroundToTransition(info.getRootLeash(), backgroundColor, startTransaction,
                        finishTransaction);
                return;
            }
        }
    }

    @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);
        return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
                wholeAnimationBounds);
    }
+6 −6
Original line number Diff line number Diff line
@@ -181,15 +181,15 @@ class ActivityEmbeddingAnimationSpec {
            @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
        final boolean isEnter = Transitions.isOpeningType(change.getMode());
        final Animation animation;
        // TODO(b/207070762): Implement edgeExtension version
        if (shouldShowBackdrop(info, change)) {
            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                    ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
                    : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
        } else {
            // Use the same edge extension animation as regular activity open.
            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                    ? com.android.internal.R.anim.task_fragment_open_enter
                    : com.android.internal.R.anim.task_fragment_open_exit);
                    ? com.android.internal.R.anim.activity_open_enter
                    : com.android.internal.R.anim.activity_open_exit);
        }
        // Use the whole animation bounds instead of the change bounds, so that when multiple change
        // targets are opening at the same time, the animation applied to each will be the same.
@@ -205,15 +205,15 @@ class ActivityEmbeddingAnimationSpec {
            @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
        final boolean isEnter = Transitions.isOpeningType(change.getMode());
        final Animation animation;
        // TODO(b/207070762): Implement edgeExtension version
        if (shouldShowBackdrop(info, change)) {
            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                    ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
                    : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
        } else {
            // Use the same edge extension animation as regular activity close.
            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                    ? com.android.internal.R.anim.task_fragment_close_enter
                    : com.android.internal.R.anim.task_fragment_close_exit);
                    ? com.android.internal.R.anim.activity_close_enter
                    : com.android.internal.R.anim.activity_close_exit);
        }
        // Use the whole animation bounds instead of the change bounds, so that when multiple change
        // targets are closing at the same time, the animation applied to each will be the same.
+1 −121
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.sDisableCustomTaskAnimationProperty;
@@ -76,10 +77,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -89,7 +87,6 @@ import android.os.IBinder;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -525,123 +522,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
        }
    }

    private void edgeExtendWindow(TransitionInfo.Change change,
            Animation a, SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction) {
        // Do not create edge extension surface for transfer starting window change.
        // The app surface could be empty thus nothing can draw on the hardware renderer, which will
        // block this thread when calling Surface#unlockCanvasAndPost.
        if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
            return;
        }
        final Transformation transformationAtStart = new Transformation();
        a.getTransformationAt(0, transformationAtStart);
        final Transformation transformationAtEnd = new Transformation();
        a.getTransformationAt(1, transformationAtEnd);

        // We want to create an extension surface that is the maximal size and the animation will
        // take care of cropping any part that overflows.
        final Insets maxExtensionInsets = Insets.min(
                transformationAtStart.getInsets(), transformationAtEnd.getInsets());

        final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(),
                change.getEndAbsBounds().height());
        final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(),
                change.getEndAbsBounds().width());
        if (maxExtensionInsets.left < 0) {
            final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
            final Rect extensionRect = new Rect(0, 0,
                    -maxExtensionInsets.left, targetSurfaceHeight);
            final int xPos = maxExtensionInsets.left;
            final int yPos = 0;
            createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
                    "Left Edge Extension", startTransaction, finishTransaction);
        }

        if (maxExtensionInsets.top < 0) {
            final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
            final Rect extensionRect = new Rect(0, 0,
                    targetSurfaceWidth, -maxExtensionInsets.top);
            final int xPos = 0;
            final int yPos = maxExtensionInsets.top;
            createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
                    "Top Edge Extension", startTransaction, finishTransaction);
        }

        if (maxExtensionInsets.right < 0) {
            final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
                    targetSurfaceWidth, targetSurfaceHeight);
            final Rect extensionRect = new Rect(0, 0,
                    -maxExtensionInsets.right, targetSurfaceHeight);
            final int xPos = targetSurfaceWidth;
            final int yPos = 0;
            createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
                    "Right Edge Extension", startTransaction, finishTransaction);
        }

        if (maxExtensionInsets.bottom < 0) {
            final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
                    targetSurfaceWidth, targetSurfaceHeight);
            final Rect extensionRect = new Rect(0, 0,
                    targetSurfaceWidth, -maxExtensionInsets.bottom);
            final int xPos = maxExtensionInsets.left;
            final int yPos = targetSurfaceHeight;
            createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
                    "Bottom Edge Extension", startTransaction, finishTransaction);
        }
    }

    private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
            Rect extensionRect, int xPos, int yPos, String layerName,
            SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction) {
        final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
                .setName(layerName)
                .setParent(surfaceToExtend)
                .setHidden(true)
                .setCallsite("DefaultTransitionHandler#startAnimation")
                .setOpaque(true)
                .setBufferSize(extensionRect.width(), extensionRect.height())
                .build();

        SurfaceControl.LayerCaptureArgs captureArgs =
                new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
                        .setSourceCrop(edgeBounds)
                        .setFrameScale(1)
                        .setPixelFormat(PixelFormat.RGBA_8888)
                        .setChildrenOnly(true)
                        .setAllowProtected(true)
                        .build();
        final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
                SurfaceControl.captureLayers(captureArgs);

        if (edgeBuffer == null) {
            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                    "Failed to capture edge of window.");
            return null;
        }

        android.graphics.BitmapShader shader =
                new android.graphics.BitmapShader(edgeBuffer.asBitmap(),
                        android.graphics.Shader.TileMode.CLAMP,
                        android.graphics.Shader.TileMode.CLAMP);
        final Paint paint = new Paint();
        paint.setShader(shader);

        final Surface surface = new Surface(edgeExtensionLayer);
        Canvas c = surface.lockHardwareCanvas();
        c.drawRect(extensionRect, paint);
        surface.unlockCanvasAndPost(c);
        surface.release();

        startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
        startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
        startTransaction.setVisibility(edgeExtensionLayer, true);
        finishTransaction.remove(edgeExtensionLayer);

        return edgeExtensionLayer;
    }

    @Nullable
    @Override
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+132 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading