diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index c6794b84c42de..3c631ad37c45f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -175,14 +175,25 @@ public class PipTransition extends PipTransitionController { return true; } + // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can + // happen when a new activity requests enter PIP). In this case, we just show this Task in + // its end state, and play other animation as normal. + final TransitionInfo.Change currentPipChange = findCurrentPipChange(info); + if (currentPipChange != null + && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) { + resetPrevPip(currentPipChange, startTransaction); + } + // Entering PIP. if (isEnteringPip(info, mCurrentPipTaskToken)) { return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback); } - // For transition that we don't animate, we may need to update the PIP surface, otherwise it - // will be reset after the transition. - updatePipForUnhandledTransition(info, startTransaction, finishTransaction); + // For transition that we don't animate, but contains the PIP leash, we need to update the + // PIP surface, otherwise it will be reset after the transition. + if (currentPipChange != null) { + updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction); + } return false; } @@ -597,30 +608,36 @@ public class PipTransition extends PipTransitionController { finishCallback.onTransitionFinished(null, null); } - private void updatePipForUnhandledTransition(@NonNull TransitionInfo info, + private void resetPrevPip(@NonNull TransitionInfo.Change prevPipChange, + @NonNull SurfaceControl.Transaction startTransaction) { + final SurfaceControl leash = prevPipChange.getLeash(); + final Rect bounds = prevPipChange.getEndAbsBounds(); + final Point offset = prevPipChange.getEndRelOffset(); + bounds.offset(-offset.x, -offset.y); + + startTransaction.setWindowCrop(leash, null); + startTransaction.setMatrix(leash, 1, 0, 0, 1); + startTransaction.setCornerRadius(leash, 0); + startTransaction.setPosition(leash, bounds.left, bounds.top); + + mCurrentPipTaskToken = null; + mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo()); + } + + private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction) { - if (mCurrentPipTaskToken == null) { - return; - } - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (!mCurrentPipTaskToken.equals(change.getContainer())) { - continue; - } - // When the PIP window is visible and being a part of the transition, such as display - // rotation, we need to update its bounds and rounded corner. - final SurfaceControl leash = change.getLeash(); - final Rect destBounds = mPipBoundsState.getBounds(); - final boolean isInPip = mPipTransitionState.isInPip(); - mSurfaceTransactionHelper - .crop(startTransaction, leash, destBounds) - .round(startTransaction, leash, isInPip); - mSurfaceTransactionHelper - .crop(finishTransaction, leash, destBounds) - .round(finishTransaction, leash, isInPip); - break; - } + // When the PIP window is visible and being a part of the transition, such as display + // rotation, we need to update its bounds and rounded corner. + final SurfaceControl leash = pipChange.getLeash(); + final Rect destBounds = mPipBoundsState.getBounds(); + final boolean isInPip = mPipTransitionState.isInPip(); + mSurfaceTransactionHelper + .crop(startTransaction, leash, destBounds) + .round(startTransaction, leash, isInPip); + mSurfaceTransactionHelper + .crop(finishTransaction, leash, destBounds) + .round(finishTransaction, leash, isInPip); } private void finishResizeForMenu(Rect destinationBounds) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 535bbb74d1e3c..3f2e975057654 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2051,19 +2051,25 @@ class RootWindowContainer extends WindowContainer try { final Task task = r.getTask(); - final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); - // This will change the root pinned task's windowing mode to its original mode, ensuring - // we only have one root task that is in pinned mode. + // If the activity in current PIP task needs to be moved back to the parent Task of next + // PIP activity, we can't use that parent Task as the next PIP Task. + // Because we need to start the Shell transition from the root Task, we delay to dismiss + // the current PIP task until root Task is ready. + boolean origPipWillBeMovedToTask = false; + final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); if (rootPinnedTask != null) { - rootPinnedTask.dismissPip(); + final ActivityRecord topPipActivity = rootPinnedTask.getTopMostActivity(); + if (topPipActivity != null && topPipActivity.getLastParentBeforePip() == task) { + origPipWillBeMovedToTask = true; + } } // Set a transition to ensure that we don't immediately try and update the visibility // of the activity entering PIP r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); - final boolean singleActivity = task.getChildCount() == 1; + final boolean singleActivity = task.getChildCount() == 1 && !origPipWillBeMovedToTask; final Task rootTask; if (singleActivity) { rootTask = task; @@ -2117,7 +2123,7 @@ class RootWindowContainer extends WindowContainer final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) && task.getDisplayContent().mAppTransition.containsTransitRequest( - TRANSIT_TO_BACK)) { + TRANSIT_TO_BACK) && !origPipWillBeMovedToTask) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } @@ -2133,6 +2139,13 @@ class RootWindowContainer extends WindowContainer } rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask); + // This will change the root pinned task's windowing mode to its original mode, ensuring + // we only have one root task that is in pinned mode. + if (rootPinnedTask != null) { + rootTask.mTransitionController.collect(rootPinnedTask); + rootPinnedTask.dismissPip(); + } + // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating // TODO(task-org): Figure-out more structured way to do this long term.