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

Commit a6a90c62 authored by Chris Li's avatar Chris Li Committed by Automerger Merge Worker
Browse files

Merge "Treat two TaskFragment leashes as one for TASK_FRAGMENT_OPEN/CLOSE"...

Merge "Treat two TaskFragment leashes as one for TASK_FRAGMENT_OPEN/CLOSE" into sc-v2-dev am: df04e229 am: 29c5ed62

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15959427

Change-Id: Ic1084e87f8bea90dcf93d71ed81dc48b90304383
parents d2664a88 29c5ed62
Loading
Loading
Loading
Loading
+119 −34
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@

package androidx.window.extensions.embedding;

import android.graphics.Point;
import static android.graphics.Matrix.MSCALE_X;

import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -25,58 +26,151 @@ import android.view.animation.Animation;
import android.view.animation.Transformation;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
 *
 * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
 */
class TaskFragmentAnimationAdapter {
    private final Animation mAnimation;
    private final RemoteAnimationTarget mTarget;
    private final SurfaceControl mLeash;
    private final boolean mSizeChanged;
    private final Point mPosition;
    private final Transformation mTransformation = new Transformation();
    private final float[] mMatrix = new float[9];
    private final float[] mVecs = new float[4];
    private final Rect mRect = new Rect();
    final Animation mAnimation;
    final RemoteAnimationTarget mTarget;
    final SurfaceControl mLeash;

    final Transformation mTransformation = new Transformation();
    final float[] mMatrix = new float[9];
    private boolean mIsFirstFrame = true;

    TaskFragmentAnimationAdapter(@NonNull Animation animation,
            @NonNull RemoteAnimationTarget target) {
        this(animation, target, target.leash, false /* sizeChanged */, null /* position */);
        this(animation, target, target.leash);
    }

    /**
     * @param sizeChanged whether the surface size needs to be changed.
     * @param leash the surface to animate.
     */
    TaskFragmentAnimationAdapter(@NonNull Animation animation,
            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
            boolean sizeChanged, @Nullable Point position) {
            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
        mAnimation = animation;
        mTarget = target;
        mLeash = leash;
        mSizeChanged = sizeChanged;
        mPosition = position != null
                ? position
                : new Point(target.localBounds.left, target.localBounds.top);
    }

    /** Called on frame update. */
    void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
    final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
        if (mIsFirstFrame) {
            t.show(mLeash);
            mIsFirstFrame = false;
        }

        currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
        mAnimation.getTransformation(currentPlayTime, mTransformation);
        mTransformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
        // Extract the transformation to the current time.
        mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
                mTransformation);
        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
        onAnimationUpdateInner(t);
    }

    /** To be overridden by subclasses to adjust the animation surface change. */
    void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
        mTransformation.getMatrix().postTranslate(
                mTarget.localBounds.left, mTarget.localBounds.top);
        t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
        t.setAlpha(mLeash, mTransformation.getAlpha());
    }

    /** Called after animation finished. */
    final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
        onAnimationUpdate(t, mAnimation.getDuration());
    }

    final long getDurationHint() {
        return mAnimation.computeDurationHint();
    }

    /**
     * Should be used when the {@link RemoteAnimationTarget} is in split with others, and want to
     * animate together as one. This adapter will offset the animation leash to make the animate of
     * two windows look like a single window.
     */
    static class SplitAdapter extends TaskFragmentAnimationAdapter {
        private final boolean mIsLeftHalf;
        private final int mWholeAnimationWidth;

        /**
         * @param isLeftHalf whether this is the left half of the animation.
         * @param wholeAnimationWidth the whole animation windows width.
         */
        SplitAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target,
                boolean isLeftHalf, int wholeAnimationWidth) {
            super(animation, target);
            mIsLeftHalf = isLeftHalf;
            mWholeAnimationWidth = wholeAnimationWidth;
            if (wholeAnimationWidth == 0) {
                throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
            }
        }

        @Override
        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
            float posX = mTarget.localBounds.left;
            final float posY = mTarget.localBounds.top;
            // This window is half of the whole animation window. Offset left/right to make it
            // look as one with the other half.
            mTransformation.getMatrix().getValues(mMatrix);
            final int targetWidth = mTarget.localBounds.width();
            final float scaleX = mMatrix[MSCALE_X];
            final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
            final float curOffset = targetWidth * (1 - scaleX) / 2;
            final float offsetDiff = totalOffset - curOffset;
            if (mIsLeftHalf) {
                posX += offsetDiff;
            } else {
                posX -= offsetDiff;
            }
            mTransformation.getMatrix().postTranslate(posX, posY);
            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
            t.setAlpha(mLeash, mTransformation.getAlpha());
        }
    }

    /**
     * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
     * size change.
     */
    static class SnapshotAdapter extends TaskFragmentAnimationAdapter {

        SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
            // Start leash is the snapshot of the starting surface.
            super(animation, target, target.startLeash);
        }

        @Override
        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
            // Snapshot should always be placed at the top left of the animation leash.
            mTransformation.getMatrix().postTranslate(0, 0);
            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
            t.setAlpha(mLeash, mTransformation.getAlpha());
        }
    }

    /**
     * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
     */
    static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
        private final float[] mVecs = new float[4];
        private final Rect mRect = new Rect();

        BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
            super(animation, target);
        }

        @Override
        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
            mTransformation.getMatrix().postTranslate(
                    mTarget.localBounds.left, mTarget.localBounds.top);
            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
            t.setAlpha(mLeash, mTransformation.getAlpha());
        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());

        if (mSizeChanged) {
            // The following applies an inverse scale to the clip-rect so that it crops "after" the
            // scale instead of before.
            mVecs[1] = mVecs[2] = 0;
@@ -92,13 +186,4 @@ class TaskFragmentAnimationAdapter {
            t.setWindowCrop(mLeash, mRect);
        }
    }

    /** Called after animation finished. */
    void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
        onAnimationUpdate(t, mAnimation.getDuration());
    }

    long getDurationHint() {
        return mAnimation.computeDurationHint();
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.window.TaskFragmentOrganizer;
class TaskFragmentAnimationController {

    private static final String TAG = "TaskFragAnimationCtrl";
    // TODO(b/196173550) turn off when finalize
    static final boolean DEBUG = false;

    private final TaskFragmentOrganizer mOrganizer;
+63 −18
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -40,6 +40,7 @@ import androidx.annotation.Nullable;

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

/** To run the TaskFragment animations. */
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
@@ -167,42 +168,86 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {

    private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
            @NonNull RemoteAnimationTarget[] targets) {
        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
        for (RemoteAnimationTarget target : targets) {
            final Animation animation =
                    mAnimationSpec.loadOpenAnimation(target.mode != MODE_CLOSING /* isEnter */);
            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
        }
        return adapters;
        return createOpenCloseAnimationAdapters(targets,
                mAnimationSpec::loadOpenAnimation);
    }

    private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
            @NonNull RemoteAnimationTarget[] targets) {
        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
        return createOpenCloseAnimationAdapters(targets,
                mAnimationSpec::loadCloseAnimation);
    }

    private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters(
            @NonNull RemoteAnimationTarget[] targets,
            @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) {
        // We need to know if the target 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.
        final List<RemoteAnimationTarget> openingTargets = new ArrayList<>();
        final List<RemoteAnimationTarget> closingTargets = new ArrayList<>();
        final Rect openingWholeScreenBounds = new Rect();
        final Rect closingWholeScreenBounds = new Rect();
        for (RemoteAnimationTarget target : targets) {
            final Animation animation =
                    mAnimationSpec.loadCloseAnimation(target.mode != MODE_CLOSING /* isEnter */);
            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
            if (target.mode != MODE_CLOSING) {
                openingTargets.add(target);
                openingWholeScreenBounds.union(target.localBounds);
            } else {
                closingTargets.add(target);
                closingWholeScreenBounds.union(target.localBounds);
            }
        }

        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
        for (RemoteAnimationTarget target : openingTargets) {
            adapters.add(createOpenCloseAnimationAdapter(target, animationProvider,
                    openingWholeScreenBounds));
        }
        for (RemoteAnimationTarget target : closingTargets) {
            adapters.add(createOpenCloseAnimationAdapter(target, animationProvider,
                    closingWholeScreenBounds));
        }
        return adapters;
    }

    private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
            @NonNull RemoteAnimationTarget target,
            @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
            @NonNull Rect wholeAnimationBounds) {
        final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
        final Rect targetBounds = target.localBounds;
        if (targetBounds.left == wholeAnimationBounds.left
                && targetBounds.right != wholeAnimationBounds.right) {
            // This is the left split of the whole animation window.
            return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
                    true /* isLeftHalf */, wholeAnimationBounds.width());
        } else if (targetBounds.left != wholeAnimationBounds.left
                && targetBounds.right == wholeAnimationBounds.right) {
            // This is the right split of the whole animation window.
            return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
                    false /* isLeftHalf */, wholeAnimationBounds.width());
        }
        // Open/close window that fills the whole animation.
        return new TaskFragmentAnimationAdapter(animation, target);
    }

    private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
            @NonNull RemoteAnimationTarget[] targets) {
        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
        for (RemoteAnimationTarget target : targets) {
            if (target.startBounds != null) {
                // This is the target with bounds change.
                final Animation[] animations =
                        mAnimationSpec.createChangeBoundsChangeAnimations(target);
                // The snapshot surface will always be at (0, 0) of its parent.
                adapters.add(new TaskFragmentAnimationAdapter(animations[0], target,
                        target.startLeash, false /* sizeChanged */, new Point(0, 0)));
                // The end surface will have size change for scaling.
                adapters.add(new TaskFragmentAnimationAdapter(animations[1], target,
                        target.leash, true /* sizeChanged */, null /* position */));
                // Adapter for the starting snapshot leash.
                adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
                        animations[0], target));
                // Adapter for the ending bounds changed leash.
                adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
                        animations[1], target));
                continue;
            }

            // These are the other targets that don't have bounds change in the same transition.
            final Animation animation;
            if (target.hasAnimatingParent) {
                // No-op if it will be covered by the changing parent window.
+16 −6
Original line number Diff line number Diff line
@@ -176,18 +176,28 @@ class TaskFragmentAnimationSpec {
        return new Animation[]{startSet, endSet};
    }

    Animation loadOpenAnimation(boolean isEnter) {
        // TODO(b/196173550) We need to customize the animation to handle two open window as one.
        return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
    Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
            @NonNull Rect wholeAnimationBounds) {
        final boolean isEnter = target.mode != MODE_CLOSING;
        final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
                ? R.styleable.WindowAnimation_activityOpenEnterAnimation
                : R.styleable.WindowAnimation_activityOpenExitAnimation);
        animation.initialize(target.localBounds.width(), target.localBounds.height(),
                wholeAnimationBounds.width(), wholeAnimationBounds.height());
        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
        return animation;
    }

    Animation loadCloseAnimation(boolean isEnter) {
        // TODO(b/196173550) We need to customize the animation to handle two open window as one.
        return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
    Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
            @NonNull Rect wholeAnimationBounds) {
        final boolean isEnter = target.mode != MODE_CLOSING;
        final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
                ? R.styleable.WindowAnimation_activityCloseEnterAnimation
                : R.styleable.WindowAnimation_activityCloseExitAnimation);
        animation.initialize(target.localBounds.width(), target.localBounds.height(),
                wholeAnimationBounds.width(), wholeAnimationBounds.height());
        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
        return animation;
    }

    private class SettingsObserver extends ContentObserver {