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

Commit d4848611 authored by Hongwei Wang's avatar Hongwei Wang
Browse files

Fix the transition from Split to PiP

- Do not bring split to top on entering PiP
  When swiping up to home (or other cases) triggers one of the
  split-screen children enters PiP, do not try to bring the other task
  to top since the pair should be considered invisible.
- When entering PiP from split screen or non-auto PiP case, use the
  alpha information in PictureInPictureSurfaceTransaction to hide the
  task which would later be turned back on to avoid flicker.

Bug: 190855091
Bug: 205894095
Video: http://recall/-/aaaaaabFQoRHlzixHdtY/42RWtayanp2qG0mHSf4Q5
Test: manual, enter PiP from split-screen, see Video
Change-Id: I1559748cd583c20d79ee458822d66e7801733d3a
parent 1ba4fcf0
Loading
Loading
Loading
Loading
+89 −29
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,9 +35,10 @@ import java.util.Objects;
 * @hide
 */
public final class PictureInPictureSurfaceTransaction implements Parcelable {
    private static final float NOT_SET = -1f;

    public final float mPositionX;
    public final float mPositionY;
    public final float mAlpha;
    public final PointF mPosition;

    public final float[] mFloat9;

@@ -45,33 +47,37 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {

    public final float mCornerRadius;

    private final Rect mWindowCrop = new Rect();
    private final Rect mWindowCrop;

    public PictureInPictureSurfaceTransaction(Parcel in) {
        mPositionX = in.readFloat();
        mPositionY = in.readFloat();
    private PictureInPictureSurfaceTransaction(Parcel in) {
        mAlpha = in.readFloat();
        mPosition = in.readTypedObject(PointF.CREATOR);
        mFloat9 = new float[9];
        in.readFloatArray(mFloat9);
        mRotation = in.readFloat();
        mCornerRadius = in.readFloat();
        mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR)));
        mWindowCrop = in.readTypedObject(Rect.CREATOR);
    }

    public PictureInPictureSurfaceTransaction(float positionX, float positionY,
            float[] float9, float rotation, float cornerRadius,
    private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
            @Nullable float[] float9, float rotation, float cornerRadius,
            @Nullable Rect windowCrop) {
        mPositionX = positionX;
        mPositionY = positionY;
        mAlpha = alpha;
        mPosition = position;
        if (float9 == null) {
            mFloat9 = new float[9];
            Matrix.IDENTITY_MATRIX.getValues(mFloat9);
            mRotation = 0;
        } else {
            mFloat9 = Arrays.copyOf(float9, 9);
            mRotation = rotation;
        mCornerRadius = cornerRadius;
        if (windowCrop != null) {
            mWindowCrop.set(windowCrop);
        }
        mCornerRadius = cornerRadius;
        mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop);
    }

    public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
        this(other.mPositionX, other.mPositionY,
        this(other.mAlpha, other.mPosition,
                other.mFloat9, other.mRotation, other.mCornerRadius, other.mWindowCrop);
    }

@@ -82,13 +88,18 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
        return matrix;
    }

    /** @return {@code true} if this transaction contains setting corner radius. */
    public boolean hasCornerRadiusSet() {
        return mCornerRadius > 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
        PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
        return Objects.equals(mPositionX, that.mPositionX)
                && Objects.equals(mPositionY, that.mPositionY)
        return Objects.equals(mAlpha, that.mAlpha)
                && Objects.equals(mPosition, that.mPosition)
                && Arrays.equals(mFloat9, that.mFloat9)
                && Objects.equals(mRotation, that.mRotation)
                && Objects.equals(mCornerRadius, that.mCornerRadius)
@@ -97,7 +108,7 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(mPositionX, mPositionY, Arrays.hashCode(mFloat9),
        return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
                mRotation, mCornerRadius, mWindowCrop);
    }

@@ -108,8 +119,8 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeFloat(mPositionX);
        out.writeFloat(mPositionY);
        out.writeFloat(mAlpha);
        out.writeTypedObject(mPosition, 0 /* flags */);
        out.writeFloatArray(mFloat9);
        out.writeFloat(mRotation);
        out.writeFloat(mCornerRadius);
@@ -120,8 +131,8 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
    public String toString() {
        final Matrix matrix = getMatrix();
        return "PictureInPictureSurfaceTransaction("
                + " posX=" + mPositionX
                + " posY=" + mPositionY
                + " alpha=" + mAlpha
                + " position=" + mPosition
                + " matrix=" + matrix.toShortString()
                + " rotation=" + mRotation
                + " cornerRadius=" + mCornerRadius
@@ -134,11 +145,20 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
            @NonNull SurfaceControl surfaceControl,
            @NonNull SurfaceControl.Transaction tx) {
        final Matrix matrix = surfaceTransaction.getMatrix();
        tx.setMatrix(surfaceControl, matrix, new float[9])
                .setPosition(surfaceControl,
                        surfaceTransaction.mPositionX, surfaceTransaction.mPositionY)
                .setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop)
                .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
        tx.setMatrix(surfaceControl, matrix, new float[9]);
        if (surfaceTransaction.mPosition != null) {
            tx.setPosition(surfaceControl,
                    surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y);
        }
        if (surfaceTransaction.mWindowCrop != null) {
            tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop);
        }
        if (surfaceTransaction.hasCornerRadiusSet()) {
            tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
        }
        if (surfaceTransaction.mAlpha != NOT_SET) {
            tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha);
        }
    }

    public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
@@ -151,4 +171,44 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
                    return new PictureInPictureSurfaceTransaction[size];
                }
            };

    public static class Builder {
        private float mAlpha = NOT_SET;
        private PointF mPosition;
        private float[] mFloat9;
        private float mRotation;
        private float mCornerRadius = NOT_SET;
        private Rect mWindowCrop;

        public Builder setAlpha(float alpha) {
            mAlpha = alpha;
            return this;
        }

        public Builder setPosition(float x, float y) {
            mPosition = new PointF(x, y);
            return this;
        }

        public Builder setTransform(@NonNull float[] float9, float rotation) {
            mFloat9 = Arrays.copyOf(float9, 9);
            mRotation = rotation;
            return this;
        }

        public Builder setCornerRadius(float cornerRadius) {
            mCornerRadius = cornerRadius;
            return this;
        }

        public Builder setWindowCrop(@NonNull Rect windowCrop) {
            mWindowCrop = new Rect(windowCrop);
            return this;
        }

        public PictureInPictureSurfaceTransaction build() {
            return new PictureInPictureSurfaceTransaction(mAlpha, mPosition,
                    mFloat9, mRotation, mCornerRadius, mWindowCrop);
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
    static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
    static final int EXIT_REASON_SCREEN_LOCKED = 7;
    static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
    static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
    @IntDef(value = {
            EXIT_REASON_UNKNOWN,
            EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -112,6 +113,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
            EXIT_REASON_ROOT_TASK_VANISHED,
            EXIT_REASON_SCREEN_LOCKED,
            EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
            EXIT_REASON_CHILD_TASK_ENTER_PIP,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface ExitReason{}
@@ -406,6 +408,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
                return "APP_FINISHED";
            case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
                return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
            case EXIT_REASON_CHILD_TASK_ENTER_PIP:
                return "CHILD_TASK_ENTER_PIP";
            default:
                return "unknown reason, reason int = " + exitReason;
        }
+19 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -629,8 +630,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        });
        mShouldUpdateRecents = false;

        mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
        mMainStage.deactivate(wct, childrenToTop == mMainStage);
        // When the exit split-screen is caused by one of the task enters auto pip,
        // we want the tasks to be put to bottom instead of top, otherwise it will end up
        // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
        final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP;
        mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage);
        mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage);
        mTaskOrganizer.applyTransaction(wct);
        mSyncQueue.runInSync(t -> t
                .setWindowCrop(mMainStage.mRootLeash, null)
@@ -660,6 +665,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            case EXIT_REASON_DRAG_DIVIDER:
            // Either of the split apps have finished
            case EXIT_REASON_APP_FINISHED:
            // One of the children enters PiP
            case EXIT_REASON_CHILD_TASK_ENTER_PIP:
                return true;
            default:
                return false;
@@ -749,6 +756,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        }
    }

    private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) {
        exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage,
                EXIT_REASON_CHILD_TASK_ENTER_PIP);
    }

    private void updateRecentTasksSplitPair() {
        if (!mShouldUpdateRecents) {
            return;
@@ -1436,6 +1448,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible);
        }

        @Override
        public void onChildTaskEnterPip(int taskId) {
            StageCoordinator.this.onStageChildTaskEnterPip(this, taskId);
        }

        @Override
        public void onRootTaskVanished() {
            reset();
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -73,6 +74,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {

        void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);

        void onChildTaskEnterPip(int taskId);

        void onRootTaskVanished();

        void onNoLongerSupportMultiWindow();
@@ -256,6 +259,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
            mChildrenTaskInfo.remove(taskId);
            mChildrenLeashes.remove(taskId);
            mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
            if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
                mCallbacks.onChildTaskEnterPip(taskId);
            }
            if (ENABLE_SHELL_TRANSITIONS) {
                // Status is managed/synchronized by the transition lifecycle.
                return;
+18 −8
Original line number Diff line number Diff line
@@ -53,8 +53,8 @@ public class PipSurfaceTransactionHelper {
        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                .setPosition(leash, positionX, positionY)
                .setCornerRadius(leash, cornerRadius);
        return new PictureInPictureSurfaceTransaction(
                positionX, positionY, mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
        return newPipSurfaceTransaction(positionX, positionY,
                mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
    }

    public PictureInPictureSurfaceTransaction scale(
@@ -70,8 +70,8 @@ public class PipSurfaceTransactionHelper {
        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                .setPosition(leash, positionX, positionY)
                .setCornerRadius(leash, cornerRadius);
        return new PictureInPictureSurfaceTransaction(
                positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds);
        return newPipSurfaceTransaction(positionX, positionY,
                mTmpFloat9, degree, cornerRadius, sourceBounds);
    }

    public PictureInPictureSurfaceTransaction scaleAndCrop(
@@ -93,8 +93,8 @@ public class PipSurfaceTransactionHelper {
                .setWindowCrop(leash, mTmpDestinationRect)
                .setPosition(leash, left, top)
                .setCornerRadius(leash, cornerRadius);
        return new PictureInPictureSurfaceTransaction(
                left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
        return newPipSurfaceTransaction(left, top,
                mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
    }

    public PictureInPictureSurfaceTransaction scaleAndRotate(
@@ -125,8 +125,7 @@ public class PipSurfaceTransactionHelper {
                .setWindowCrop(leash, mTmpDestinationRect)
                .setPosition(leash, adjustedPositionX, adjustedPositionY)
                .setCornerRadius(leash, cornerRadius);
        return new PictureInPictureSurfaceTransaction(
                adjustedPositionX, adjustedPositionY,
        return newPipSurfaceTransaction(adjustedPositionX, adjustedPositionY,
                mTmpFloat9, degree, cornerRadius, mTmpDestinationRect);
    }

@@ -137,6 +136,17 @@ public class PipSurfaceTransactionHelper {
        return mCornerRadius * scale;
    }

    private static PictureInPictureSurfaceTransaction newPipSurfaceTransaction(
            float posX, float posY, float[] float9, float rotation, float cornerRadius,
            Rect windowCrop) {
        return new PictureInPictureSurfaceTransaction.Builder()
                .setPosition(posX, posY)
                .setTransform(float9, rotation)
                .setCornerRadius(cornerRadius)
                .setWindowCrop(windowCrop)
                .build();
    }

    /** @return {@link SurfaceControl.Transaction} instance with vsync-id */
    public static SurfaceControl.Transaction newSurfaceControlTransaction() {
        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
Loading