diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 117971fa4e741..dbe20753929b2 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -394,6 +394,11 @@ public interface WindowManager extends ViewManager { * @hide */ int TRANSIT_KEYGUARD_UNOCCLUDE = 9; + /** + * A window is starting to enter PiP. + * @hide + */ + int TRANSIT_PIP = 10; /** * The first slot for custom transition types. Callers (like Shell) can make use of custom * transition types for dealing with special cases. These types are effectively ignored by @@ -403,7 +408,7 @@ public interface WindowManager extends ViewManager { * implementation. * @hide */ - int TRANSIT_FIRST_CUSTOM = 10; + int TRANSIT_FIRST_CUSTOM = 11; /** * @hide @@ -419,6 +424,7 @@ public interface WindowManager extends ViewManager { TRANSIT_KEYGUARD_GOING_AWAY, TRANSIT_KEYGUARD_OCCLUDE, TRANSIT_KEYGUARD_UNOCCLUDE, + TRANSIT_PIP, TRANSIT_FIRST_CUSTOM }) @Retention(RetentionPolicy.SOURCE) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 27707a4132599..25b68d590037a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -463,6 +463,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void removePipImmediately() { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mToken, null); + wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); + wct.reorder(mToken, false); + mPipTransitionController.startTransition(null, wct); + return; + } + try { // Reset the task bounds first to ensure the activity configuration is reset as well final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -524,6 +533,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { mPipMenuController.attach(mLeash); + } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mOneShotAnimationType = ANIM_TYPE_BOUNDS; } return; } 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 93b0fcfcd9d78..b75cde099bcbb 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 @@ -18,6 +18,7 @@ package com.android.wm.shell.pip; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.WindowManager.TRANSIT_PIP; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; @@ -27,6 +28,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI 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_REMOVE_PIP; import android.app.TaskInfo; import android.content.Context; @@ -73,10 +75,28 @@ public class PipTransition extends PipTransitionController { .getInteger(R.integer.config_pipResizeAnimationDuration); } + @Override + public void setIsFullAnimation(boolean isFullAnimation) { + setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA); + } + + /** + * Sets the preferred animation type for one time. + * This is typically used to set the animation type to + * {@link PipAnimationController#ANIM_TYPE_ALPHA}. + */ + private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { + mOneShotAnimationType = animationType; + } + @Override public void startTransition(Rect destinationBounds, WindowContainerTransaction out) { - mExitDestinationBounds.set(destinationBounds); - mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this); + if (destinationBounds != null) { + mExitDestinationBounds.set(destinationBounds); + mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this); + } else { + mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this); + } } @Override @@ -96,6 +116,14 @@ public class PipTransition extends PipTransitionController { return success; } + if (info.getType() == TRANSIT_REMOVE_PIP) { + startTransaction.apply(); + finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(), + mPipBoundsState.getDisplayBounds()); + finishCallback.onTransitionFinished(null, null); + return true; + } + // Search for an Enter PiP transition (along with a show wallpaper one) TransitionInfo.Change enterPip = null; TransitionInfo.Change wallpaper = null; @@ -129,13 +157,23 @@ public class PipTransition extends PipTransitionController { @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - return null; + if (request.getType() == TRANSIT_PIP) { + WindowContainerTransaction wct = new WindowContainerTransaction(); + mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + wct.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED); + wct.setBounds(request.getTriggerTask().token, destinationBounds); + return wct; + } else { + return null; + } } @Override public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx) { + if (isInPipDirection(direction)) { mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); } @@ -185,6 +223,9 @@ public class PipTransition extends PipTransitionController { 0 /* startingAngle */, Surface.ROTATION_0); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { startTransaction.setAlpha(leash, 0f); + // PiP menu is attached late in the process here to avoid any artifacts on the leash + // caused by addShellRoot when in gesture navigation mode. + mPipMenuController.attach(leash); animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); mOneShotAnimationType = ANIM_TYPE_BOUNDS; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index bc262560844df..dedc56678ef17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -100,6 +100,15 @@ public abstract class PipTransitionController implements Transitions.TransitionH SurfaceControl.Transaction tx) { } + /** + * Called to inform the transition that the animation should start with the assumption that + * PiP is not animating from its original bounds, but rather a continuation of another + * animation. For example, gesture navigation would first fade out the PiP activity, and the + * transition should be responsible to animate in (such as fade in) the PiP. + */ + public void setIsFullAnimation(boolean isFullAnimation) { + } + /** * Called when the Shell wants to starts a transition/animation. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 4f3ec96968b29..62b50c55e77cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -67,6 +67,7 @@ import com.android.wm.shell.pip.IPip; import com.android.wm.shell.pip.IPipAnimationListener; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; @@ -528,6 +529,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private void setPinnedStackAnimationType(int animationType) { mPipTaskOrganizer.setOneShotAnimationType(animationType); + mPipTransitionController.setIsFullAnimation( + animationType == PipAnimationController.ANIM_TYPE_BOUNDS); } private void setPinnedStackAnimationListener(IPipAnimationListener callback) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 64faed2061091..8ed92dfb39c67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -77,9 +77,12 @@ public class Transitions implements RemoteCallable { /** Transition type for launching 2 tasks simultaneously. */ public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2; - /** Transition type for exiting PIP via the Shell, either via Expand or Dismiss. */ + /** Transition type for exiting PIP via the Shell, via pressing the expand button. */ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3; + /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */ + public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4; + private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d7731beedf10c..37e4fc16cb753 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4530,6 +4530,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + boolean getDeferHidingClient() { + return mDeferHidingClient; + } + @Override boolean isVisible() { // If the activity isn't hidden then it is considered visible and there is no need to check @@ -5248,13 +5252,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A supportsEnterPipOnTaskSwitch = false; break; case RESUMED: - // If the app is capable of entering PIP, we should try pausing it now - // so it can PIP correctly. - if (deferHidingClient) { - getTaskFragment().startPausing(false /* uiSleeping */, - null /* resuming */, "makeInvisible"); - break; - } case INITIALIZING: case PAUSING: case PAUSED: diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index f8cae6b0e7210..4414db3bc2d01 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -36,10 +36,10 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -2177,7 +2177,7 @@ class RootWindowContainer extends WindowContainer // display area, so reparent. rootTask.reparent(taskDisplayArea, true /* onTop */); } - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask); + mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask); // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index de3af2cb058cc..7169988f10f68 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -300,6 +300,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar != null && !ar.isVisibleRequested()) { + // If activity is capable of entering PiP, give it a chance to enter it now. + if (ar.getDeferHidingClient() && ar.getTask() != null) { + mController.mAtm.mTaskSupervisor.mUserLeaving = true; + ar.getTaskFragment().startPausing(false /* uiSleeping */, null /* resuming */, + "finishTransition"); + mController.mAtm.mTaskSupervisor.mUserLeaving = false; + } ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Commit activity becoming invisible: %s", ar); ar.commitVisibility(false /* visible */, false /* performLayout */);