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

Commit 62f0464d authored by Chris Li's avatar Chris Li
Browse files

Provide startBounds for closing container with legacy transition

Before, we only provide startBounds when the animation target is
MODE_CHANGING, with which we won't know the start bounds for target that
is closing while resizing.

Now, we provide startBounds for all targets, so that we can use it to
provide seamless animation when the target is closing while resizing.

Also make sure the closing container's surface is in the start bounds
before the animation starts to avoid any flicker.

Bug: 241043533
Test: Verify with switching between split and stack.
Change-Id: I4d8e1ee44d418249dcb434425c7ab3d63a429bad
parent 0672ae40
Loading
Loading
Loading
Loading
+19 −10
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;

import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
@@ -175,10 +176,16 @@ public class RemoteAnimationTarget implements Parcelable {
    public final Rect screenSpaceBounds;

    /**
     * The starting bounds of the source container in screen space coordinates. This is {@code null}
     * if the animation target isn't MODE_CHANGING. Since this is the starting bounds, it's size
     * should be equivalent to the size of the starting thumbnail. Note that sourceContainerBounds
     * is the end bounds of a change transition.
     * The starting bounds of the source container in screen space coordinates.
     * For {@link #MODE_OPENING}, this will be equivalent to {@link #screenSpaceBounds}.
     * For {@link #MODE_CLOSING}, this will be equivalent to {@link #screenSpaceBounds} unless the
     * closing container is also resizing. For example, when ActivityEmbedding split pair becomes
     * stacked, the container on the back will be resized to fullscreen, but will also be covered
     * (closing) by the container in the front.
     * For {@link #MODE_CHANGING}, since this is the starting bounds, its size should be equivalent
     * to the bounds of the starting thumbnail.
     *
     * Note that {@link #screenSpaceBounds} is the end bounds of a transition.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public final Rect startBounds;
@@ -247,7 +254,8 @@ public class RemoteAnimationTarget implements Parcelable {
            Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
            Rect localBounds, Rect screenSpaceBounds,
            WindowConfiguration windowConfig, boolean isNotInRecents,
            SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo,
            SurfaceControl startLeash, @Nullable Rect startBounds,
            ActivityManager.RunningTaskInfo taskInfo,
            boolean allowEnterPip) {
        this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
                position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
@@ -258,7 +266,7 @@ public class RemoteAnimationTarget implements Parcelable {
            Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
            Rect localBounds, Rect screenSpaceBounds,
            WindowConfiguration windowConfig, boolean isNotInRecents,
            SurfaceControl startLeash, Rect startBounds,
            SurfaceControl startLeash, @Nullable Rect startBounds,
            ActivityManager.RunningTaskInfo taskInfo, boolean allowEnterPip,
            @WindowManager.LayoutParams.WindowType int windowType) {
        this.mode = mode;
@@ -275,10 +283,13 @@ public class RemoteAnimationTarget implements Parcelable {
        this.windowConfiguration = windowConfig;
        this.isNotInRecents = isNotInRecents;
        this.startLeash = startLeash;
        this.startBounds = startBounds == null ? null : new Rect(startBounds);
        this.taskInfo = taskInfo;
        this.allowEnterPip = allowEnterPip;
        this.windowType = windowType;
        // Same as screenSpaceBounds if the window is not resizing.
        this.startBounds = startBounds == null
                ? new Rect(screenSpaceBounds)
                : new Rect(startBounds);
    }

    public RemoteAnimationTarget(Parcel in) {
@@ -399,9 +410,7 @@ public class RemoteAnimationTarget implements Parcelable {
        if (startLeash != null) {
            startLeash.dumpDebug(proto, START_LEASH);
        }
        if (startBounds != null) {
        startBounds.dumpDebug(proto, START_BOUNDS);
        }
        proto.end(token);
    }

+30 −5
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package androidx.window.extensions.embedding;

import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;

import android.graphics.Point;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -49,6 +51,16 @@ class TaskFragmentAnimationAdapter {
    /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
    @NonNull
    private final Rect mWholeAnimationBounds = new Rect();
    /**
     * Area in absolute coordinate that should represent all the content to show for this window.
     * This should be the end bounds for opening window, and start bounds for closing window in case
     * the window is resizing during the open/close transition.
     */
    @NonNull
    private final Rect mContentBounds = new Rect();
    /** Offset relative to the window parent surface for {@link #mContentBounds}. */
    @NonNull
    private final Point mContentRelOffset = new Point();

    @NonNull
    final Transformation mTransformation = new Transformation();
@@ -78,6 +90,21 @@ class TaskFragmentAnimationAdapter {
        mTarget = target;
        mLeash = leash;
        mWholeAnimationBounds.set(wholeAnimationBounds);
        if (target.mode == MODE_CLOSING) {
            // When it is closing, we want to show the content at the start position in case the
            // window is resizing as well. For example, when the activities is changing from split
            // to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
            final Rect startBounds = target.startBounds;
            final Rect endBounds = target.screenSpaceBounds;
            mContentBounds.set(startBounds);
            mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
            mContentRelOffset.offset(
                    startBounds.left - endBounds.left,
                    startBounds.top - endBounds.top);
        } else {
            mContentBounds.set(target.screenSpaceBounds);
            mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
        }
    }

    /**
@@ -108,8 +135,7 @@ class TaskFragmentAnimationAdapter {
    /** To be overridden by subclasses to adjust the animation surface change. */
    void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
        // Update the surface position and alpha.
        mTransformation.getMatrix().postTranslate(
                mTarget.localBounds.left, mTarget.localBounds.top);
        mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
        t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
        t.setAlpha(mLeash, mTransformation.getAlpha());

@@ -117,9 +143,8 @@ class TaskFragmentAnimationAdapter {
        // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
        final int positionX = Math.round(mMatrix[MTRANS_X]);
        final int positionY = Math.round(mMatrix[MTRANS_Y]);
        final Rect cropRect = new Rect(mTarget.screenSpaceBounds);
        final Rect localBounds = mTarget.localBounds;
        cropRect.offset(positionX - localBounds.left, positionY - localBounds.top);
        final Rect cropRect = new Rect(mContentBounds);
        cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);

        // Store the current offset of the surface top left from (0,0) in absolute coordinate.
        final int offsetX = cropRect.left;
+2 −1
Original line number Diff line number Diff line
@@ -17,6 +17,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.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
@@ -254,7 +255,7 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
            @NonNull RemoteAnimationTarget[] targets) {
        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
        for (RemoteAnimationTarget target : targets) {
            if (target.startBounds != null) {
            if (target.mode == MODE_CHANGING) {
                // This is the target with bounds change.
                final Animation[] animations =
                        mAnimationSpec.createChangeBoundsChangeAnimations(target);
+2 −2
Original line number Diff line number Diff line
@@ -114,8 +114,8 @@ class TaskFragmentAnimationSpec {
    @NonNull
    Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
        // TODO(b/258126915): we want to keep track of the closing start bounds
        final Rect bounds = target.screenSpaceBounds;
        // Use startBounds if the window is closing in case it may also resize.
        final Rect bounds = target.startBounds;
        final int endTop;
        final int endLeft;
        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+20 −0
Original line number Diff line number Diff line
@@ -268,6 +268,7 @@ public class AppTransitionController {
            handleClosingApps();
            handleOpeningApps();
            handleChangingApps(transit);
            handleClosingChangingContainers();

            appTransition.setLastAppTransition(transit, topOpeningApp,
                    topClosingApp, topChangingApp);
@@ -287,6 +288,7 @@ public class AppTransitionController {
        mDisplayContent.mClosingApps.clear();
        mDisplayContent.mChangingContainers.clear();
        mDisplayContent.mUnknownAppVisibilityController.clear();
        mDisplayContent.mClosingChangingContainers.clear();

        // This has changed the visibility of windows, so perform
        // a new layout to get them all up-to-date.
@@ -1171,6 +1173,24 @@ public class AppTransitionController {
        }
    }

    private void handleClosingChangingContainers() {
        final ArrayMap<WindowContainer, Rect> containers =
                mDisplayContent.mClosingChangingContainers;
        while (!containers.isEmpty()) {
            final WindowContainer container = containers.keyAt(0);
            containers.remove(container);

            // For closing changing windows that are part of the transition, they should have been
            // removed from mClosingChangingContainers in WindowContainer#getAnimationAdapter()
            // If the closing changing TaskFragment is not part of the transition, update its
            // surface after removing it from mClosingChangingContainers.
            final TaskFragment taskFragment = container.asTaskFragment();
            if (taskFragment != null) {
                taskFragment.updateOrganizedTaskFragmentSurface();
            }
        }
    }

    private void handleChangingApps(@TransitionOldType int transit) {
        final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
        final int appsCount = apps.size();
Loading