Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -460,6 +460,24 @@ public class PipBoundsAlgorithm { return adjustedNormalBounds; 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. * Dumps internal states. */ */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +4 −2 Original line number Original line Diff line number Diff line Loading @@ -226,10 +226,12 @@ public abstract class Pip2Module { static PipDisplayTransferHandler providePipDisplayTransferHandler(Context context, static PipDisplayTransferHandler providePipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState, PipTransitionState pipTransitionState, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipBoundsState pipBoundsState, DisplayController displayController PipBoundsState pipBoundsState, DisplayController displayController, PipDisplayLayoutState pipDisplayLayoutState, PipBoundsAlgorithm pipBoundsAlgorithm ) { ) { return new PipDisplayTransferHandler(context, pipTransitionState, pipScheduler, return new PipDisplayTransferHandler(context, pipTransitionState, pipScheduler, rootTaskDisplayAreaOrganizer, pipBoundsState, displayController); rootTaskDisplayAreaOrganizer, pipBoundsState, displayController, pipDisplayLayoutState, pipBoundsAlgorithm); } } @WMSingleton @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +3 −24 Original line number Original line Diff line number Diff line Loading @@ -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. * Resizes the pip window and updates user-resized bounds. * * Loading @@ -561,11 +548,8 @@ public class PipResizeGestureHandler { void userResizeTo(Rect bounds, float snapFraction) { void userResizeTo(Rect bounds, float snapFraction) { Rect finalBounds = new Rect(bounds); 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 // 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 // apply the requested snap fraction onto the target bounds mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction); mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction); Loading Loading @@ -597,15 +581,10 @@ public class PipResizeGestureHandler { resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y); 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 // snap mLastResizeBounds to the correct edge based on movement bounds snapToMovementBoundsEdge(mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.snapToMovementBoundsEdge(mLastResizeBounds); final float snapFraction = mPipBoundsAlgorithm.getSnapFraction( final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds); mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); // disable any touch events beyond resizing too // disable any touch events beyond resizing too Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandler.java +101 −17 Original line number Original line Diff line number Diff line Loading @@ -15,7 +15,11 @@ */ */ package com.android.wm.shell.pip2.phone; 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.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; import android.content.Context; import android.graphics.Rect; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.RectF; Loading @@ -27,12 +31,17 @@ import android.view.SurfaceControl.Transaction; import androidx.annotation.NonNull; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator; 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.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; 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 * Handler for moving PiP window to another display when the device is connected to external Loading @@ -53,14 +62,21 @@ public class PipDisplayTransferHandler implements private final DisplayController mDisplayController; private final DisplayController mDisplayController; private final PipTransitionState mPipTransitionState; private final PipTransitionState mPipTransitionState; private final PipScheduler mPipScheduler; private final PipScheduler mPipScheduler; private final Context mContext; private final PipDisplayLayoutState mPipDisplayLayoutState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; @VisibleForTesting boolean mWaitingForDisplayTransfer; @VisibleForTesting boolean mWaitingForDisplayTransfer; @VisibleForTesting @VisibleForTesting ArrayMap<Integer, SurfaceControl> mOnDragMirrorPerDisplayId = new ArrayMap<>(); ArrayMap<Integer, SurfaceControl> mOnDragMirrorPerDisplayId = new ArrayMap<>(); private int mTargetDisplayId; private PipResizeAnimatorSupplier mPipResizeAnimatorSupplier; public PipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState, public PipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipBoundsState pipBoundsState, DisplayController displayController) { PipBoundsState pipBoundsState, DisplayController displayController, PipDisplayLayoutState pipDisplayLayoutState, PipBoundsAlgorithm pipBoundsAlgorithm) { mContext = context; mPipTransitionState = pipTransitionState; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this); mPipTransitionState.addPipTransitionStateChangedListener(this); mPipScheduler = pipScheduler; mPipScheduler = pipScheduler; Loading @@ -70,12 +86,20 @@ public class PipDisplayTransferHandler implements mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context); mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context); mPipBoundsState = pipBoundsState; mPipBoundsState = pipBoundsState; mDisplayController = displayController; 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(); Bundle extra = new Bundle(); extra.putInt(ORIGIN_DISPLAY_ID_KEY, originDisplayId); extra.putInt(ORIGIN_DISPLAY_ID_KEY, originDisplayId); extra.putInt(TARGET_DISPLAY_ID_KEY, targetDisplayId); extra.putInt(TARGET_DISPLAY_ID_KEY, targetDisplayId); extra.putParcelable(PIP_DESTINATION_BOUNDS, destinationBounds); mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); } } Loading @@ -83,36 +107,77 @@ public class PipDisplayTransferHandler implements @Override @Override public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { if (extra == null) return; switch (newState) { switch (newState) { case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: if (extra == null || !extra.containsKey(ORIGIN_DISPLAY_ID_KEY) if (!extra.containsKey(ORIGIN_DISPLAY_ID_KEY) || !extra.containsKey( || !extra.containsKey(TARGET_DISPLAY_ID_KEY)) { 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; break; } } mWaitingForDisplayTransfer = true; mPipScheduler.scheduleMoveToDisplay(extra.getInt(ORIGIN_DISPLAY_ID_KEY), mWaitingForDisplayTransfer = true; extra.getInt(TARGET_DISPLAY_ID_KEY)); mPipScheduler.scheduleMoveToDisplay(mTargetDisplayId, extra.getParcelable(PIP_DESTINATION_BOUNDS, Rect.class)); break; break; case PipTransitionState.CHANGING_PIP_BOUNDS: case PipTransitionState.CHANGING_PIP_BOUNDS: if (extra == null || !mWaitingForDisplayTransfer) { if (!mWaitingForDisplayTransfer) { break; 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( final Transaction startTx = extra.getParcelable( PipTransition.PIP_START_TX, Transaction.class); PipTransition.PIP_START_TX, Transaction.class); final Rect destinationBounds = extra.getParcelable( final Transaction finishTx = extra.getParcelable( PipTransition.PIP_DESTINATION_BOUNDS, Rect.class); 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) { mPipSurfaceTransactionHelper.round(startTx, pipLeash, true).shadow(startTx, if (startTx == null) return; 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(); mPipDisplayLayoutState.setDisplayId(mTargetDisplayId); mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds); 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; } } } /** /** Loading Loading @@ -188,4 +253,23 @@ public class PipDisplayTransferHandler implements void setSurfaceTransactionHelper(PipSurfaceTransactionHelper surfaceTransactionHelper) { void setSurfaceTransactionHelper(PipSurfaceTransactionHelper surfaceTransactionHelper) { mPipSurfaceTransactionHelper = 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; } } } libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java +6 −1 Original line number Original line Diff line number Diff line Loading @@ -140,6 +140,8 @@ public class PipInputConsumer { final InputChannel inputChannel = new InputChannel(); final InputChannel inputChannel = new InputChannel(); try { try { final int displayId = mPipDisplayLayoutState.getDisplayId(); 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.destroyInputConsumer(mToken, displayId); mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel); mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel); } catch (RemoteException e) { } catch (RemoteException e) { Loading @@ -163,7 +165,10 @@ public class PipInputConsumer { return; return; } } try { 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) { } catch (RemoteException e) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Failed to destroy input consumer, %s", TAG, e); "%s: Failed to destroy input consumer, %s", TAG, e); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -460,6 +460,24 @@ public class PipBoundsAlgorithm { return adjustedNormalBounds; 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. * Dumps internal states. */ */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +4 −2 Original line number Original line Diff line number Diff line Loading @@ -226,10 +226,12 @@ public abstract class Pip2Module { static PipDisplayTransferHandler providePipDisplayTransferHandler(Context context, static PipDisplayTransferHandler providePipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState, PipTransitionState pipTransitionState, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipBoundsState pipBoundsState, DisplayController displayController PipBoundsState pipBoundsState, DisplayController displayController, PipDisplayLayoutState pipDisplayLayoutState, PipBoundsAlgorithm pipBoundsAlgorithm ) { ) { return new PipDisplayTransferHandler(context, pipTransitionState, pipScheduler, return new PipDisplayTransferHandler(context, pipTransitionState, pipScheduler, rootTaskDisplayAreaOrganizer, pipBoundsState, displayController); rootTaskDisplayAreaOrganizer, pipBoundsState, displayController, pipDisplayLayoutState, pipBoundsAlgorithm); } } @WMSingleton @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +3 −24 Original line number Original line Diff line number Diff line Loading @@ -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. * Resizes the pip window and updates user-resized bounds. * * Loading @@ -561,11 +548,8 @@ public class PipResizeGestureHandler { void userResizeTo(Rect bounds, float snapFraction) { void userResizeTo(Rect bounds, float snapFraction) { Rect finalBounds = new Rect(bounds); 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 // 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 // apply the requested snap fraction onto the target bounds mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction); mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction); Loading Loading @@ -597,15 +581,10 @@ public class PipResizeGestureHandler { resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y); 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 // snap mLastResizeBounds to the correct edge based on movement bounds snapToMovementBoundsEdge(mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.snapToMovementBoundsEdge(mLastResizeBounds); final float snapFraction = mPipBoundsAlgorithm.getSnapFraction( final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds); mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); // disable any touch events beyond resizing too // disable any touch events beyond resizing too Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDisplayTransferHandler.java +101 −17 Original line number Original line Diff line number Diff line Loading @@ -15,7 +15,11 @@ */ */ package com.android.wm.shell.pip2.phone; 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.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; import android.content.Context; import android.graphics.Rect; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.RectF; Loading @@ -27,12 +31,17 @@ import android.view.SurfaceControl.Transaction; import androidx.annotation.NonNull; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator; 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.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; 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 * Handler for moving PiP window to another display when the device is connected to external Loading @@ -53,14 +62,21 @@ public class PipDisplayTransferHandler implements private final DisplayController mDisplayController; private final DisplayController mDisplayController; private final PipTransitionState mPipTransitionState; private final PipTransitionState mPipTransitionState; private final PipScheduler mPipScheduler; private final PipScheduler mPipScheduler; private final Context mContext; private final PipDisplayLayoutState mPipDisplayLayoutState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; @VisibleForTesting boolean mWaitingForDisplayTransfer; @VisibleForTesting boolean mWaitingForDisplayTransfer; @VisibleForTesting @VisibleForTesting ArrayMap<Integer, SurfaceControl> mOnDragMirrorPerDisplayId = new ArrayMap<>(); ArrayMap<Integer, SurfaceControl> mOnDragMirrorPerDisplayId = new ArrayMap<>(); private int mTargetDisplayId; private PipResizeAnimatorSupplier mPipResizeAnimatorSupplier; public PipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState, public PipDisplayTransferHandler(Context context, PipTransitionState pipTransitionState, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipScheduler pipScheduler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, PipBoundsState pipBoundsState, DisplayController displayController) { PipBoundsState pipBoundsState, DisplayController displayController, PipDisplayLayoutState pipDisplayLayoutState, PipBoundsAlgorithm pipBoundsAlgorithm) { mContext = context; mPipTransitionState = pipTransitionState; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this); mPipTransitionState.addPipTransitionStateChangedListener(this); mPipScheduler = pipScheduler; mPipScheduler = pipScheduler; Loading @@ -70,12 +86,20 @@ public class PipDisplayTransferHandler implements mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context); mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context); mPipBoundsState = pipBoundsState; mPipBoundsState = pipBoundsState; mDisplayController = displayController; 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(); Bundle extra = new Bundle(); extra.putInt(ORIGIN_DISPLAY_ID_KEY, originDisplayId); extra.putInt(ORIGIN_DISPLAY_ID_KEY, originDisplayId); extra.putInt(TARGET_DISPLAY_ID_KEY, targetDisplayId); extra.putInt(TARGET_DISPLAY_ID_KEY, targetDisplayId); extra.putParcelable(PIP_DESTINATION_BOUNDS, destinationBounds); mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); } } Loading @@ -83,36 +107,77 @@ public class PipDisplayTransferHandler implements @Override @Override public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { if (extra == null) return; switch (newState) { switch (newState) { case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: if (extra == null || !extra.containsKey(ORIGIN_DISPLAY_ID_KEY) if (!extra.containsKey(ORIGIN_DISPLAY_ID_KEY) || !extra.containsKey( || !extra.containsKey(TARGET_DISPLAY_ID_KEY)) { 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; break; } } mWaitingForDisplayTransfer = true; mPipScheduler.scheduleMoveToDisplay(extra.getInt(ORIGIN_DISPLAY_ID_KEY), mWaitingForDisplayTransfer = true; extra.getInt(TARGET_DISPLAY_ID_KEY)); mPipScheduler.scheduleMoveToDisplay(mTargetDisplayId, extra.getParcelable(PIP_DESTINATION_BOUNDS, Rect.class)); break; break; case PipTransitionState.CHANGING_PIP_BOUNDS: case PipTransitionState.CHANGING_PIP_BOUNDS: if (extra == null || !mWaitingForDisplayTransfer) { if (!mWaitingForDisplayTransfer) { break; 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( final Transaction startTx = extra.getParcelable( PipTransition.PIP_START_TX, Transaction.class); PipTransition.PIP_START_TX, Transaction.class); final Rect destinationBounds = extra.getParcelable( final Transaction finishTx = extra.getParcelable( PipTransition.PIP_DESTINATION_BOUNDS, Rect.class); 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) { mPipSurfaceTransactionHelper.round(startTx, pipLeash, true).shadow(startTx, if (startTx == null) return; 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(); mPipDisplayLayoutState.setDisplayId(mTargetDisplayId); mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds); 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; } } } /** /** Loading Loading @@ -188,4 +253,23 @@ public class PipDisplayTransferHandler implements void setSurfaceTransactionHelper(PipSurfaceTransactionHelper surfaceTransactionHelper) { void setSurfaceTransactionHelper(PipSurfaceTransactionHelper surfaceTransactionHelper) { mPipSurfaceTransactionHelper = 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; } } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java +6 −1 Original line number Original line Diff line number Diff line Loading @@ -140,6 +140,8 @@ public class PipInputConsumer { final InputChannel inputChannel = new InputChannel(); final InputChannel inputChannel = new InputChannel(); try { try { final int displayId = mPipDisplayLayoutState.getDisplayId(); 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.destroyInputConsumer(mToken, displayId); mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel); mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel); } catch (RemoteException e) { } catch (RemoteException e) { Loading @@ -163,7 +165,10 @@ public class PipInputConsumer { return; return; } } try { 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) { } catch (RemoteException e) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Failed to destroy input consumer, %s", TAG, e); "%s: Failed to destroy input consumer, %s", TAG, e); Loading