Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +57 −23 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.TransitionInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; Loading Loading @@ -416,17 +417,32 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_LEAVE_PIP; final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); if (Transitions.ENABLE_SHELL_TRANSITIONS && direction == TRANSITION_DIRECTION_LEAVE_PIP) { // When exit to fullscreen with Shell transition enabled, we update the Task windowing // mode directly so that it can also trigger display rotation and visibility update in // the same transition if there will be any. wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); // We can inherit the parent bounds as it is going to be fullscreen. The // destinationBounds calculated above will be incorrect if this is with rotation. wct.setBounds(mToken, null); } else { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); // We set to fullscreen here for now, but later it will be set to UNDEFINED for // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN); wct.setBounds(mToken, destinationBounds); wct.setBoundsChangeTransaction(mToken, tx); } // Set the exiting state first so if there is fixed rotation later, the running animation // won't be interrupted by alpha animation for existing PiP. mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); Loading Loading @@ -729,24 +745,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } if (Transitions.ENABLE_SHELL_TRANSITIONS && mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP) { // With Shell transition, we do the cleanup in PipTransition after exiting PIP. return; } final WindowContainerToken token = info.token; Objects.requireNonNull(token, "Requires valid WindowContainerToken"); if (token.asBinder() != mToken.asBinder()) { Log.wtf(TAG, "Unrecognized token: " + token); return; } clearWaitForFixedRotation(); mPipTransitionState.setInSwipePipToHomeTransition(false); mPictureInPictureParams = null; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); mPipMenuController.detach(); if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } onExitPipFinished(info); if (Transitions.ENABLE_SHELL_TRANSITIONS) { mPipTransitionController.forceFinishTransition(); Loading Loading @@ -843,6 +853,30 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, clearWaitForFixedRotation(); } /** Called when exiting PIP tranisiton is finished to do the state cleanup. */ void onExitPipFinished(TaskInfo info) { clearWaitForFixedRotation(); mPipTransitionState.setInSwipePipToHomeTransition(false); mPictureInPictureParams = null; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); mPipMenuController.detach(); if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } } /** Whether the given {@link TransitionInfo.Change} is for the PIP window. */ boolean isPipChange(TransitionInfo.Change change) { if (mToken == null) { Log.e(TAG, "We no longer keep track of the PIP token"); } return mToken.equals(change.getContainer()); } private void fadeExistingPip(boolean show) { final float alphaStart = show ? 0 : 1; final float alphaEnd = show ? 1 : 0; Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +91 −44 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.util.RotationUtils.deltaRotation; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; Loading @@ -30,6 +32,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT; import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; import static com.android.wm.shell.transition.Transitions.isOpeningType; Loading @@ -38,9 +41,9 @@ import android.app.ActivityManager; import android.app.TaskInfo; import android.content.Context; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; import android.util.Log; import android.view.Surface; import android.view.SurfaceControl; import android.window.TransitionInfo; Loading Loading @@ -72,7 +75,7 @@ public class PipTransition extends PipTransitionController { private final Optional<SplitScreenController> mSplitScreenOptional; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; private Rect mExitDestinationBounds = new Rect(); private final Rect mExitDestinationBounds = new Rect(); private IBinder mExitTransition = null; public PipTransition(Context context, Loading Loading @@ -119,50 +122,46 @@ public class PipTransition extends PipTransitionController { } @Override public boolean startAnimation(@android.annotation.NonNull IBinder transition, @android.annotation.NonNull TransitionInfo info, @android.annotation.NonNull SurfaceControl.Transaction startTransaction, @android.annotation.NonNull SurfaceControl.Transaction finishTransaction, @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) { public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { final int type = info.getType(); if (mExitTransition == transition) { if (transition.equals(mExitTransition)) { mExitDestinationBounds.setEmpty(); mExitTransition = null; if (type == TRANSIT_EXIT_PIP_TO_SPLIT) { return startExitToSplitAnimation( info, startTransaction, finishTransaction, finishCallback); } if (info.getChanges().size() == 1) { if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null, null); mFinishCallback = null; throw new RuntimeException("Previous callback not called, aborting exit PIP."); } final TransitionInfo.Change change = info.getChanges().get(0); mFinishCallback = finishCallback; startTransaction.apply(); boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(), new Rect(mExitDestinationBounds)); mExitDestinationBounds.setEmpty(); return success; } else { Log.e(TAG, "Got an exit-pip transition with unexpected change-list"); } } if (type == TRANSIT_REMOVE_PIP) { if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; throw new RuntimeException("Previous callback not called, aborting remove PIP."); final TransitionInfo.Change exitPipChange = findPipChange(info); if (exitPipChange == null) { throw new RuntimeException("Cannot find the pip window for exit-pip transition."); } switch (type) { case TRANSIT_EXIT_PIP: startExitAnimation(info, startTransaction, finishCallback, exitPipChange); break; case TRANSIT_EXIT_PIP_TO_SPLIT: startExitToSplitAnimation(info, startTransaction, finishTransaction, finishCallback, exitPipChange); break; case TRANSIT_REMOVE_PIP: startTransaction.apply(); finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(), mPipBoundsState.getDisplayBounds()); mPipOrganizer.onExitPipFinished(exitPipChange.getTaskInfo()); finishCallback.onTransitionFinished(null, null); break; default: throw new IllegalStateException("mExitTransition with unexpected transit type=" + transitTypeToString(type)); } return true; } Loading Loading @@ -298,7 +297,55 @@ public class PipTransition extends PipTransitionController { mFinishCallback = null; } private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, @Nullable private TransitionInfo.Change findPipChange(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getTaskInfo() != null && mPipOrganizer.isPipChange(change)) { return change; } } return null; } private void startExitAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change pipChange) { TransitionInfo.Change displayRotationChange = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0 && change.getStartRotation() != change.getEndRotation()) { displayRotationChange = change; break; } } if (displayRotationChange != null) { // Exiting PIP to fullscreen with orientation change. // TODO(b/210965919): actual rotation animation } // When there is no rotation, we can simply expand the PIP window. mFinishCallback = (wct, wctCB) -> { mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(wct, wctCB); }; // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); final Point offset = pipChange.getEndRelOffset(); destinationBounds.offset(-offset.x, -offset.y); startTransaction.setWindowCrop(pipChange.getLeash(), destinationBounds); mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(), destinationBounds, mPipBoundsState.getBounds()); startTransaction.apply(); startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds); } private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, final Rect destinationBounds) { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), Loading @@ -309,8 +356,6 @@ public class PipTransition extends PipTransitionController { .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .start(); return true; } private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, Loading Loading @@ -387,10 +432,11 @@ public class PipTransition extends PipTransitionController { return true; } private boolean startExitToSplitAnimation(TransitionInfo info, private void startExitToSplitAnimation(TransitionInfo info, SurfaceControl.Transaction startTransaction, SurfaceControl.Transaction finishTransaction, Transitions.TransitionFinishCallback finishCallback) { Transitions.TransitionFinishCallback finishCallback, TransitionInfo.Change pipChange) { final int changeSize = info.getChanges().size(); if (changeSize < 4) { throw new RuntimeException( Loading @@ -417,8 +463,9 @@ public class PipTransition extends PipTransitionController { } mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction); startTransaction.apply(); mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(null, null); return true; } private void finishResizeForMenu(Rect destinationBounds) { Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +57 −23 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.TransitionInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; Loading Loading @@ -416,17 +417,32 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_LEAVE_PIP; final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); if (Transitions.ENABLE_SHELL_TRANSITIONS && direction == TRANSITION_DIRECTION_LEAVE_PIP) { // When exit to fullscreen with Shell transition enabled, we update the Task windowing // mode directly so that it can also trigger display rotation and visibility update in // the same transition if there will be any. wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); // We can inherit the parent bounds as it is going to be fullscreen. The // destinationBounds calculated above will be incorrect if this is with rotation. wct.setBounds(mToken, null); } else { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); // We set to fullscreen here for now, but later it will be set to UNDEFINED for // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN); wct.setBounds(mToken, destinationBounds); wct.setBoundsChangeTransaction(mToken, tx); } // Set the exiting state first so if there is fixed rotation later, the running animation // won't be interrupted by alpha animation for existing PiP. mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); Loading Loading @@ -729,24 +745,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } if (Transitions.ENABLE_SHELL_TRANSITIONS && mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP) { // With Shell transition, we do the cleanup in PipTransition after exiting PIP. return; } final WindowContainerToken token = info.token; Objects.requireNonNull(token, "Requires valid WindowContainerToken"); if (token.asBinder() != mToken.asBinder()) { Log.wtf(TAG, "Unrecognized token: " + token); return; } clearWaitForFixedRotation(); mPipTransitionState.setInSwipePipToHomeTransition(false); mPictureInPictureParams = null; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); mPipMenuController.detach(); if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } onExitPipFinished(info); if (Transitions.ENABLE_SHELL_TRANSITIONS) { mPipTransitionController.forceFinishTransition(); Loading Loading @@ -843,6 +853,30 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, clearWaitForFixedRotation(); } /** Called when exiting PIP tranisiton is finished to do the state cleanup. */ void onExitPipFinished(TaskInfo info) { clearWaitForFixedRotation(); mPipTransitionState.setInSwipePipToHomeTransition(false); mPictureInPictureParams = null; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); mPipMenuController.detach(); if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } } /** Whether the given {@link TransitionInfo.Change} is for the PIP window. */ boolean isPipChange(TransitionInfo.Change change) { if (mToken == null) { Log.e(TAG, "We no longer keep track of the PIP token"); } return mToken.equals(change.getContainer()); } private void fadeExistingPip(boolean show) { final float alphaStart = show ? 0 : 1; final float alphaEnd = show ? 1 : 0; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +91 −44 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.util.RotationUtils.deltaRotation; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; Loading @@ -30,6 +32,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT; import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; import static com.android.wm.shell.transition.Transitions.isOpeningType; Loading @@ -38,9 +41,9 @@ import android.app.ActivityManager; import android.app.TaskInfo; import android.content.Context; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; import android.util.Log; import android.view.Surface; import android.view.SurfaceControl; import android.window.TransitionInfo; Loading Loading @@ -72,7 +75,7 @@ public class PipTransition extends PipTransitionController { private final Optional<SplitScreenController> mSplitScreenOptional; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; private Rect mExitDestinationBounds = new Rect(); private final Rect mExitDestinationBounds = new Rect(); private IBinder mExitTransition = null; public PipTransition(Context context, Loading Loading @@ -119,50 +122,46 @@ public class PipTransition extends PipTransitionController { } @Override public boolean startAnimation(@android.annotation.NonNull IBinder transition, @android.annotation.NonNull TransitionInfo info, @android.annotation.NonNull SurfaceControl.Transaction startTransaction, @android.annotation.NonNull SurfaceControl.Transaction finishTransaction, @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) { public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { final int type = info.getType(); if (mExitTransition == transition) { if (transition.equals(mExitTransition)) { mExitDestinationBounds.setEmpty(); mExitTransition = null; if (type == TRANSIT_EXIT_PIP_TO_SPLIT) { return startExitToSplitAnimation( info, startTransaction, finishTransaction, finishCallback); } if (info.getChanges().size() == 1) { if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null, null); mFinishCallback = null; throw new RuntimeException("Previous callback not called, aborting exit PIP."); } final TransitionInfo.Change change = info.getChanges().get(0); mFinishCallback = finishCallback; startTransaction.apply(); boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(), new Rect(mExitDestinationBounds)); mExitDestinationBounds.setEmpty(); return success; } else { Log.e(TAG, "Got an exit-pip transition with unexpected change-list"); } } if (type == TRANSIT_REMOVE_PIP) { if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; throw new RuntimeException("Previous callback not called, aborting remove PIP."); final TransitionInfo.Change exitPipChange = findPipChange(info); if (exitPipChange == null) { throw new RuntimeException("Cannot find the pip window for exit-pip transition."); } switch (type) { case TRANSIT_EXIT_PIP: startExitAnimation(info, startTransaction, finishCallback, exitPipChange); break; case TRANSIT_EXIT_PIP_TO_SPLIT: startExitToSplitAnimation(info, startTransaction, finishTransaction, finishCallback, exitPipChange); break; case TRANSIT_REMOVE_PIP: startTransaction.apply(); finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(), mPipBoundsState.getDisplayBounds()); mPipOrganizer.onExitPipFinished(exitPipChange.getTaskInfo()); finishCallback.onTransitionFinished(null, null); break; default: throw new IllegalStateException("mExitTransition with unexpected transit type=" + transitTypeToString(type)); } return true; } Loading Loading @@ -298,7 +297,55 @@ public class PipTransition extends PipTransitionController { mFinishCallback = null; } private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, @Nullable private TransitionInfo.Change findPipChange(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getTaskInfo() != null && mPipOrganizer.isPipChange(change)) { return change; } } return null; } private void startExitAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change pipChange) { TransitionInfo.Change displayRotationChange = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0 && change.getStartRotation() != change.getEndRotation()) { displayRotationChange = change; break; } } if (displayRotationChange != null) { // Exiting PIP to fullscreen with orientation change. // TODO(b/210965919): actual rotation animation } // When there is no rotation, we can simply expand the PIP window. mFinishCallback = (wct, wctCB) -> { mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(wct, wctCB); }; // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); final Point offset = pipChange.getEndRelOffset(); destinationBounds.offset(-offset.x, -offset.y); startTransaction.setWindowCrop(pipChange.getLeash(), destinationBounds); mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(), destinationBounds, mPipBoundsState.getBounds()); startTransaction.apply(); startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds); } private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, final Rect destinationBounds) { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), Loading @@ -309,8 +356,6 @@ public class PipTransition extends PipTransitionController { .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .start(); return true; } private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, Loading Loading @@ -387,10 +432,11 @@ public class PipTransition extends PipTransitionController { return true; } private boolean startExitToSplitAnimation(TransitionInfo info, private void startExitToSplitAnimation(TransitionInfo info, SurfaceControl.Transaction startTransaction, SurfaceControl.Transaction finishTransaction, Transitions.TransitionFinishCallback finishCallback) { Transitions.TransitionFinishCallback finishCallback, TransitionInfo.Change pipChange) { final int changeSize = info.getChanges().size(); if (changeSize < 4) { throw new RuntimeException( Loading @@ -417,8 +463,9 @@ public class PipTransition extends PipTransitionController { } mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction); startTransaction.apply(); mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(null, null); return true; } private void finishResizeForMenu(Rect destinationBounds) { Loading