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

Commit d50fcd72 authored by Vania Desmonda's avatar Vania Desmonda
Browse files

Make PiP window functional after moving displays.

Demo: https://drive.google.com/file/d/10p1WIkcYVgERWVNnGmtJxqZOGjHXAPkM/view?usp=sharing

Bug: 383403514
Test: manual test and atest
Flag: com.android.window.flags.enable_dragging_pip_across_displays
Change-Id: I6a7e2e3a76afa5e1e60554b16b539b940264a920
parent 6a369500
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -460,6 +460,24 @@ public class PipBoundsAlgorithm {
        return adjustedNormalBounds;
    }

    /**
     * Snaps PiP bounds to its movement bounds.
     */
    public void snapToMovementBoundsEdge(Rect bounds) {
        // Get the current movement bounds
        final Rect movementBounds = getMovementBounds(bounds);
        final int leftEdge = bounds.left;

        final int fromLeft = Math.abs(leftEdge - movementBounds.left);
        final int fromRight = Math.abs(movementBounds.right - leftEdge);

        // The PIP will be snapped to either the right or left edge, so calculate which one
        // is closest to the current position.
        final int newLeft = fromLeft < fromRight
                ? movementBounds.left : movementBounds.right;

        bounds.offsetTo(newLeft, bounds.top);
    }
    /**
     * Dumps internal states.
     */
+4 −2
Original line number Diff line number Diff line
@@ -226,10 +226,12 @@ public abstract class Pip2Module {
    static PipDisplayTransferHandler providePipDisplayTransferHandler(Context context,
            PipTransitionState pipTransitionState,
            PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            PipBoundsState pipBoundsState, DisplayController displayController
            PipBoundsState pipBoundsState, DisplayController displayController,
            PipDisplayLayoutState pipDisplayLayoutState, PipBoundsAlgorithm pipBoundsAlgorithm
    ) {
        return new PipDisplayTransferHandler(context, pipTransitionState, pipScheduler,
                rootTaskDisplayAreaOrganizer, pipBoundsState, displayController);
                rootTaskDisplayAreaOrganizer, pipBoundsState, displayController,
                pipDisplayLayoutState, pipBoundsAlgorithm);
    }

    @WMSingleton
+3 −24
Original line number Diff line number Diff line
@@ -537,21 +537,8 @@ public class PipResizeGestureHandler {
        }
    }

    private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) {
        final int leftEdge = bounds.left;


        final int fromLeft = Math.abs(leftEdge - movementBounds.left);
        final int fromRight = Math.abs(movementBounds.right - leftEdge);

        // The PIP will be snapped to either the right or left edge, so calculate which one
        // is closest to the current position.
        final int newLeft = fromLeft < fromRight
                ? movementBounds.left : movementBounds.right;

        bounds.offsetTo(newLeft, mLastResizeBounds.top);
    }

    /**
     * Resizes the pip window and updates user-resized bounds.
     *
@@ -561,11 +548,8 @@ public class PipResizeGestureHandler {
    void userResizeTo(Rect bounds, float snapFraction) {
        Rect finalBounds = new Rect(bounds);

        // get the current movement bounds
        final Rect movementBounds = mPipBoundsAlgorithm.getMovementBounds(finalBounds);

        // snap the target bounds to the either left or right edge, by choosing the closer one
        snapToMovementBoundsEdge(finalBounds, movementBounds);
        mPipBoundsAlgorithm.snapToMovementBoundsEdge(bounds);

        // apply the requested snap fraction onto the target bounds
        mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction);
@@ -597,15 +581,10 @@ public class PipResizeGestureHandler {
                    resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
                }

                // get the current movement bounds
                final Rect movementBounds = mPipBoundsAlgorithm
                        .getMovementBounds(mLastResizeBounds);

                // snap mLastResizeBounds to the correct edge based on movement bounds
                snapToMovementBoundsEdge(mLastResizeBounds, movementBounds);
                mPipBoundsAlgorithm.snapToMovementBoundsEdge(mLastResizeBounds);

                final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
                        mLastResizeBounds, movementBounds);
                final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds);
                mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);

                // disable any touch events beyond resizing too
+101 −17
Original line number Diff line number Diff line
@@ -15,7 +15,11 @@
 */
package com.android.wm.shell.pip2.phone;

import static com.android.wm.shell.pip2.phone.PipTransition.ANIMATING_BOUNDS_CHANGE_DURATION;
import static com.android.wm.shell.pip2.phone.PipTransition.PIP_DESTINATION_BOUNDS;

import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -27,12 +31,17 @@ import android.view.SurfaceControl.Transaction;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

/**
 * Handler for moving PiP window to another display when the device is connected to external
@@ -53,14 +62,21 @@ public class PipDisplayTransferHandler implements
    private final DisplayController mDisplayController;
    private final PipTransitionState mPipTransitionState;
    private final PipScheduler mPipScheduler;
    private final Context mContext;
    private final PipDisplayLayoutState mPipDisplayLayoutState;
    private final PipBoundsAlgorithm mPipBoundsAlgorithm;

    @VisibleForTesting boolean mWaitingForDisplayTransfer;
    @VisibleForTesting
    ArrayMap<Integer, SurfaceControl> mOnDragMirrorPerDisplayId = new ArrayMap<>();
    private int mTargetDisplayId;
    private PipResizeAnimatorSupplier mPipResizeAnimatorSupplier;

    public PipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState,
            PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            PipBoundsState pipBoundsState, DisplayController displayController) {
            PipBoundsState pipBoundsState, DisplayController displayController,
            PipDisplayLayoutState pipDisplayLayoutState, PipBoundsAlgorithm pipBoundsAlgorithm) {
        mContext = context;
        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this);
        mPipScheduler = pipScheduler;
@@ -70,12 +86,20 @@ public class PipDisplayTransferHandler implements
        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
        mPipBoundsState = pipBoundsState;
        mDisplayController = displayController;
        mPipDisplayLayoutState = pipDisplayLayoutState;
        mPipBoundsAlgorithm = pipBoundsAlgorithm;
        mPipResizeAnimatorSupplier = PipResizeAnimator::new;
    }

    void scheduleMovePipToDisplay(int originDisplayId, int targetDisplayId) {
    void scheduleMovePipToDisplay(int originDisplayId, int targetDisplayId,
            Rect destinationBounds) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                "%s scheduleMovePipToDisplay from=%d to=%d", TAG, originDisplayId, targetDisplayId);

        Bundle extra = new Bundle();
        extra.putInt(ORIGIN_DISPLAY_ID_KEY, originDisplayId);
        extra.putInt(TARGET_DISPLAY_ID_KEY, targetDisplayId);
        extra.putParcelable(PIP_DESTINATION_BOUNDS, destinationBounds);

        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
    }
@@ -83,36 +107,77 @@ public class PipDisplayTransferHandler implements
    @Override
    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
        if (extra == null) return;

        switch (newState) {
            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
                if (extra == null || !extra.containsKey(ORIGIN_DISPLAY_ID_KEY)
                        || !extra.containsKey(TARGET_DISPLAY_ID_KEY)) {
                if (!extra.containsKey(ORIGIN_DISPLAY_ID_KEY) || !extra.containsKey(
                        TARGET_DISPLAY_ID_KEY)) {
                    break;
                }

                final int originDisplayId = extra.getInt(ORIGIN_DISPLAY_ID_KEY);
                mTargetDisplayId = extra.getInt(TARGET_DISPLAY_ID_KEY);
                if (originDisplayId == mTargetDisplayId) {
                    break;
                }
                mWaitingForDisplayTransfer = true;

                mPipScheduler.scheduleMoveToDisplay(extra.getInt(ORIGIN_DISPLAY_ID_KEY),
                        extra.getInt(TARGET_DISPLAY_ID_KEY));
                mWaitingForDisplayTransfer = true;
                mPipScheduler.scheduleMoveToDisplay(mTargetDisplayId,
                        extra.getParcelable(PIP_DESTINATION_BOUNDS, Rect.class));
                break;
            case PipTransitionState.CHANGING_PIP_BOUNDS:
                if (extra == null || !mWaitingForDisplayTransfer) {
                if (!mWaitingForDisplayTransfer) {
                    break;
                }
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                        "%s Animating PiP display change to=%d", TAG, mTargetDisplayId);

                SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
                TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
                final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
                        PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
                final Transaction startTx = extra.getParcelable(
                        PipTransition.PIP_START_TX, Transaction.class);
                final Rect destinationBounds = extra.getParcelable(
                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
                final Transaction finishTx = extra.getParcelable(
                        PipTransition.PIP_FINISH_TX, Transaction.class);
                final Rect pipBounds = extra.getParcelable(
                        PIP_DESTINATION_BOUNDS, Rect.class);

                startMoveToDisplayAnimation(startTx, destinationBounds);
        }
    }
                Rect finalBounds = new Rect(pipBounds);
                mPipBoundsAlgorithm.snapToMovementBoundsEdge(finalBounds);

    private void startMoveToDisplayAnimation(Transaction startTx, Rect destinationBounds) {
        if (startTx == null) return;
                mPipSurfaceTransactionHelper.round(startTx, pipLeash, true).shadow(startTx,
                        pipLeash, true /* applyShadowRadius */);
                // Set state to exiting and exited PiP to unregister input consumer on the current
                // display.
                // TODO(b/414864788): Refactor transition states setting during display transfer
                mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
                mPipTransitionState.setState(PipTransitionState.EXITED_PIP);

        startTx.apply();
        mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds);
                mPipDisplayLayoutState.setDisplayId(mTargetDisplayId);
                mPipDisplayLayoutState.setDisplayLayout(mDisplayController.getDisplayLayout(
                        mTargetDisplayId));
                mPipTransitionState.setPinnedTaskLeash(pipLeash);
                mPipTransitionState.setPipTaskInfo(taskInfo);

                final PipResizeAnimator animator = mPipResizeAnimatorSupplier.get(mContext,
                        mPipSurfaceTransactionHelper, pipLeash, startTx, finishTx,
                        pipBounds, pipBounds, finalBounds, duration, 0);

                animator.setAnimationEndCallback(() -> {
                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                            "%s Finished animating PiP display change to=%d", TAG,
                            mTargetDisplayId);
                    mPipScheduler.scheduleFinishPipBoundsChange(finalBounds);
                    // Set state to ENTERED_PIP to register input consumer on the target display
                    mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
                    mPipBoundsState.setHasUserResizedPip(true);
                    mWaitingForDisplayTransfer = false;
                });
                animator.start();
                break;
        }
    }

    /**
@@ -188,4 +253,23 @@ public class PipDisplayTransferHandler implements
    void setSurfaceTransactionHelper(PipSurfaceTransactionHelper surfaceTransactionHelper) {
        mPipSurfaceTransactionHelper = surfaceTransactionHelper;
    }

    @VisibleForTesting
    interface PipResizeAnimatorSupplier {
        PipResizeAnimator get(@NonNull Context context,
                @NonNull PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
                @NonNull SurfaceControl leash,
                @Nullable SurfaceControl.Transaction startTx,
                @Nullable SurfaceControl.Transaction finishTx,
                @NonNull Rect baseBounds,
                @NonNull Rect startBounds,
                @NonNull Rect endBounds,
                int duration,
                float delta);
    }

    @VisibleForTesting
    void setPipResizeAnimatorSupplier(@NonNull PipResizeAnimatorSupplier supplier) {
        mPipResizeAnimatorSupplier = supplier;
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -140,6 +140,8 @@ public class PipInputConsumer {
        final InputChannel inputChannel = new InputChannel();
        try {
            final int displayId = mPipDisplayLayoutState.getDisplayId();
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Creating input consumer on displayID: %d", TAG, displayId);
            mWindowManager.destroyInputConsumer(mToken, displayId);
            mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel);
        } catch (RemoteException e) {
@@ -163,7 +165,10 @@ public class PipInputConsumer {
            return;
        }
        try {
            mWindowManager.destroyInputConsumer(mToken, mPipDisplayLayoutState.getDisplayId());
            final int displayId = mPipDisplayLayoutState.getDisplayId();
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Destroying input consumer on displayID: %d", TAG, displayId);
            mWindowManager.destroyInputConsumer(mToken, displayId);
        } catch (RemoteException e) {
            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Failed to destroy input consumer, %s", TAG, e);
Loading