Split MixedTransition into Recents and Default.
4/4 to split the the Recents functionality from the rest of
DefaultMixedHandler logic.
Bug: 314971702
Flag: NA
Test: https://android-build.corp.google.com/builds/abtd/run/L79000030000829051
Change-Id: I52885c355e3a561c361c1c3993188b6a1a1f848f
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index bf783e6..8c2203e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -21,17 +21,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
import static android.view.WindowManager.TRANSIT_PIP;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
-import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
-import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
import android.annotation.NonNull;
@@ -56,7 +48,6 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentsTransitionHandler;
-import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellInit;
@@ -84,7 +75,7 @@
private UnfoldTransitionHandler mUnfoldHandler;
private ActivityEmbeddingController mActivityEmbeddingController;
- private static class MixedTransition {
+ abstract static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
/** Both the display and split-state (enter/exit) is changing */
@@ -124,15 +115,11 @@
int mAnimType = ANIM_TYPE_DEFAULT;
final IBinder mTransition;
- private final Transitions mPlayer;
- private final DefaultMixedHandler mMixedHandler;
- private final PipTransitionController mPipHandler;
- private final RecentsTransitionHandler mRecentsHandler;
- private final StageCoordinator mSplitHandler;
- private final KeyguardTransitionHandler mKeyguardHandler;
- private final DesktopTasksController mDesktopTasksController;
- private final UnfoldTransitionHandler mUnfoldHandler;
- private final ActivityEmbeddingController mActivityEmbeddingController;
+ protected final Transitions mPlayer;
+ protected final DefaultMixedHandler mMixedHandler;
+ protected final PipTransitionController mPipHandler;
+ protected final StageCoordinator mSplitHandler;
+ protected final KeyguardTransitionHandler mKeyguardHandler;
Transitions.TransitionHandler mLeftoversHandler = null;
TransitionInfo mInfo = null;
@@ -156,409 +143,33 @@
MixedTransition(int type, IBinder transition, Transitions player,
DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
- RecentsTransitionHandler recentsHandler, StageCoordinator splitHandler,
- KeyguardTransitionHandler keyguardHandler,
- DesktopTasksController desktopTasksController,
- UnfoldTransitionHandler unfoldHandler,
- ActivityEmbeddingController activityEmbeddingController) {
+ StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler) {
mType = type;
mTransition = transition;
mPlayer = player;
mMixedHandler = mixedHandler;
mPipHandler = pipHandler;
- mRecentsHandler = recentsHandler;
mSplitHandler = splitHandler;
mKeyguardHandler = keyguardHandler;
- mDesktopTasksController = desktopTasksController;
- mUnfoldHandler = unfoldHandler;
- mActivityEmbeddingController = activityEmbeddingController;
-
- switch (type) {
- case TYPE_RECENTS_DURING_DESKTOP:
- case TYPE_RECENTS_DURING_KEYGUARD:
- case TYPE_RECENTS_DURING_SPLIT:
- mLeftoversHandler = mRecentsHandler;
- break;
- case TYPE_UNFOLD:
- mLeftoversHandler = mUnfoldHandler;
- break;
- case TYPE_DISPLAY_AND_SPLIT_CHANGE:
- case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
- case TYPE_ENTER_PIP_FROM_SPLIT:
- case TYPE_KEYGUARD:
- case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
- default:
- break;
- }
}
- boolean startAnimation(
+ abstract boolean startAnimation(
@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- switch (mType) {
- case TYPE_ENTER_PIP_FROM_SPLIT:
- return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
- finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
- case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
- return animateEnterPipFromActivityEmbedding(
- info, startTransaction, finishTransaction, finishCallback);
- case TYPE_DISPLAY_AND_SPLIT_CHANGE:
- return false;
- case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
- final boolean handledToPip = animateOpenIntentWithRemoteAndPip(
- info, startTransaction, finishTransaction, finishCallback);
- // Consume the transition on remote handler if the leftover handler already
- // handle this transition. And if it cannot, the transition will be handled by
- // remote handler, so don't consume here.
- // Need to check leftOverHandler as it may change in
- // #animateOpenIntentWithRemoteAndPip
- if (handledToPip && mHasRequestToRemote
- && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
- mPlayer.getRemoteTransitionHandler().onTransitionConsumed(
- transition, false, null);
- }
- return handledToPip;
- case TYPE_RECENTS_DURING_SPLIT:
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- // Pip auto-entering info might be appended to recent transition like
- // pressing home-key in 3-button navigation. This offers split handler the
- // opportunity to handle split to pip animation.
- if (mPipHandler.isEnteringPip(change, info.getType())
- && mSplitHandler.getSplitItemPosition(change.getLastParent())
- != SPLIT_POSITION_UNDEFINED) {
- return animateEnterPipFromSplit(
- this, info, startTransaction, finishTransaction, finishCallback,
- mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
- }
- }
+ @NonNull Transitions.TransitionFinishCallback finishCallback);
- return animateRecentsDuringSplit(
- info, startTransaction, finishTransaction, finishCallback);
- case TYPE_KEYGUARD:
- return animateKeyguard(this, info, startTransaction, finishTransaction,
- finishCallback, mKeyguardHandler, mPipHandler);
- case TYPE_RECENTS_DURING_KEYGUARD:
- return animateRecentsDuringKeyguard(
- info, startTransaction, finishTransaction, finishCallback);
- case TYPE_RECENTS_DURING_DESKTOP:
- return animateRecentsDuringDesktop(
- info, startTransaction, finishTransaction, finishCallback);
- case TYPE_UNFOLD:
- return animateUnfold(
- info, startTransaction, finishTransaction, finishCallback);
- default:
- throw new IllegalStateException(
- "Starting mixed animation without a known mixed type? " + mType);
- }
- }
-
- private boolean animateEnterPipFromActivityEmbedding(
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
- + "entering PIP from an Activity Embedding window");
- // Split into two transitions (wct)
- TransitionInfo.Change pipChange = null;
- final TransitionInfo everythingElse =
- subCopy(info, TRANSIT_TO_BACK, true /* changes */);
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- TransitionInfo.Change change = info.getChanges().get(i);
- if (mPipHandler.isEnteringPip(change, info.getType())) {
- if (pipChange != null) {
- throw new IllegalStateException("More than 1 pip-entering changes in one"
- + " transition? " + info);
- }
- pipChange = change;
- // going backwards, so remove-by-index is fine.
- everythingElse.getChanges().remove(i);
- }
- }
-
- final Transitions.TransitionFinishCallback finishCB = (wct) -> {
- --mInFlightSubAnimations;
- joinFinishArgs(wct);
- if (mInFlightSubAnimations > 0) return;
- finishCallback.onTransitionFinished(mFinishWCT);
- };
-
- if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
- // Fallback to dispatching to other handlers.
- return false;
- }
-
- // PIP window should always be on the highest Z order.
- if (pipChange != null) {
- mInFlightSubAnimations = 2;
- mPipHandler.startEnterAnimation(
- pipChange,
- startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
- finishTransaction,
- finishCB);
- } else {
- mInFlightSubAnimations = 1;
- }
-
- mActivityEmbeddingController.startAnimation(mTransition, everythingElse,
- startTransaction, finishTransaction, finishCB);
- return true;
- }
-
- private boolean animateOpenIntentWithRemoteAndPip(
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- TransitionInfo.Change pipChange = null;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- TransitionInfo.Change change = info.getChanges().get(i);
- if (mPipHandler.isEnteringPip(change, info.getType())) {
- if (pipChange != null) {
- throw new IllegalStateException("More than 1 pip-entering changes in one"
- + " transition? " + info);
- }
- pipChange = change;
- info.getChanges().remove(i);
- }
- }
- Transitions.TransitionFinishCallback finishCB = (wct) -> {
- --mInFlightSubAnimations;
- joinFinishArgs(wct);
- if (mInFlightSubAnimations > 0) return;
- finishCallback.onTransitionFinished(mFinishWCT);
- };
- if (pipChange == null) {
- if (mLeftoversHandler != null) {
- mInFlightSubAnimations = 1;
- if (mLeftoversHandler.startAnimation(
- mTransition, info, startTransaction, finishTransaction, finishCB)) {
- return true;
- }
- }
- return false;
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
- + " animation because remote-animation likely doesn't support it");
- // Split the transition into 2 parts: the pip part and the rest.
- mInFlightSubAnimations = 2;
- // make a new startTransaction because pip's startEnterAnimation "consumes" it so
- // we need a separate one to send over to launcher.
- SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
-
- mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
-
- // Dispatch the rest of the transition normally.
- if (mLeftoversHandler != null
- && mLeftoversHandler.startAnimation(
- mTransition, info, startTransaction, finishTransaction, finishCB)) {
- return true;
- }
- mLeftoversHandler = mPlayer.dispatchTransition(mTransition, info,
- startTransaction, finishTransaction, finishCB, mMixedHandler);
- return true;
- }
-
- private boolean animateRecentsDuringSplit(
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- // Split-screen is only interested in the recents transition finishing (and merging), so
- // just wrap finish and start recents animation directly.
- Transitions.TransitionFinishCallback finishCB = (wct) -> {
- mInFlightSubAnimations = 0;
- // If pair-to-pair switching, the post-recents clean-up isn't needed.
- wct = wct != null ? wct : new WindowContainerTransaction();
- if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
- mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
- } else {
- // notify pair-to-pair recents animation finish
- mSplitHandler.onRecentsPairToPairAnimationFinish(wct);
- }
- mSplitHandler.onTransitionAnimationComplete();
- finishCallback.onTransitionFinished(wct);
- };
- mInFlightSubAnimations = 1;
- mSplitHandler.onRecentsInSplitAnimationStart(info);
- final boolean handled = mLeftoversHandler.startAnimation(mTransition, info,
- startTransaction, finishTransaction, finishCB);
- if (!handled) {
- mSplitHandler.onRecentsInSplitAnimationCanceled();
- }
- return handled;
- }
-
- private boolean animateRecentsDuringKeyguard(
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (mInfo == null) {
- mInfo = info;
- mFinishT = finishTransaction;
- mFinishCB = finishCallback;
- }
- return startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction);
- }
-
- private boolean animateRecentsDuringDesktop(
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- Transitions.TransitionFinishCallback finishCB = wct -> {
- mInFlightSubAnimations--;
- if (mInFlightSubAnimations == 0) {
- finishCallback.onTransitionFinished(wct);
- }
- };
-
- mInFlightSubAnimations++;
- boolean consumed = mRecentsHandler.startAnimation(
- mTransition, info, startTransaction, finishTransaction, finishCB);
- if (!consumed) {
- mInFlightSubAnimations--;
- return false;
- }
- if (mDesktopTasksController != null) {
- mDesktopTasksController.syncSurfaceState(info, finishTransaction);
- return true;
- }
-
- return false;
- }
-
- private boolean animateUnfold(
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- final Transitions.TransitionFinishCallback finishCB = (wct) -> {
- mInFlightSubAnimations--;
- if (mInFlightSubAnimations > 0) return;
- finishCallback.onTransitionFinished(wct);
- };
- mInFlightSubAnimations = 1;
- // Sync pip state.
- if (mPipHandler != null) {
- mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
- }
- if (mSplitHandler != null && mSplitHandler.isSplitActive()) {
- mSplitHandler.updateSurfaces(startTransaction);
- }
- return mUnfoldHandler.startAnimation(
- mTransition, info, startTransaction, finishTransaction, finishCB);
- }
-
- void mergeAnimation(
+ abstract void mergeAnimation(
@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- switch (mType) {
- case TYPE_DISPLAY_AND_SPLIT_CHANGE:
- // queue since no actual animation.
- break;
- case TYPE_ENTER_PIP_FROM_SPLIT:
- if (mAnimType == ANIM_TYPE_GOING_HOME) {
- boolean ended = mSplitHandler.end();
- // If split couldn't end (because it is remote), then don't end everything
- // else since we have to play out the animation anyways.
- if (!ended) return;
- mPipHandler.end();
- if (mLeftoversHandler != null) {
- mLeftoversHandler.mergeAnimation(
- transition, info, t, mergeTarget, finishCallback);
- }
- } else {
- mPipHandler.end();
- }
- break;
- case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
- mPipHandler.end();
- mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
- finishCallback);
- break;
- case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
- mPipHandler.end();
- if (mLeftoversHandler != null) {
- mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
- finishCallback);
- }
- break;
- case TYPE_RECENTS_DURING_SPLIT:
- if (mSplitHandler.isPendingEnter(transition)) {
- // Recents -> enter-split means that we are switching from one pair to
- // another pair.
- mAnimType = ANIM_TYPE_PAIR_TO_PAIR;
- }
- mLeftoversHandler.mergeAnimation(
- transition, info, t, mergeTarget, finishCallback);
- break;
- case TYPE_KEYGUARD:
- mKeyguardHandler.mergeAnimation(
- transition, info, t, mergeTarget, finishCallback);
- break;
- case TYPE_RECENTS_DURING_KEYGUARD:
- if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
- DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT);
- if (animateKeyguard(this, info, t, mFinishT, mFinishCB, mKeyguardHandler,
- mPipHandler)) {
- finishCallback.onTransitionFinished(null);
- }
- }
- mLeftoversHandler.mergeAnimation(
- transition, info, t, mergeTarget, finishCallback);
- break;
- case TYPE_RECENTS_DURING_DESKTOP:
- mLeftoversHandler.mergeAnimation(
- transition, info, t, mergeTarget, finishCallback);
- break;
- case TYPE_UNFOLD:
- mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
- break;
- default:
- throw new IllegalStateException(
- "Playing a mixed transition with unknown type? " + mType);
- }
- }
+ @NonNull Transitions.TransitionFinishCallback finishCallback);
- void onTransitionConsumed(
+ abstract void onTransitionConsumed(
@NonNull IBinder transition, boolean aborted,
- @Nullable SurfaceControl.Transaction finishT) {
- switch (mType) {
- case TYPE_ENTER_PIP_FROM_SPLIT:
- mPipHandler.onTransitionConsumed(transition, aborted, finishT);
- break;
- case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
- mPipHandler.onTransitionConsumed(transition, aborted, finishT);
- mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
- break;
- case TYPE_RECENTS_DURING_SPLIT:
- case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
- case TYPE_RECENTS_DURING_DESKTOP:
- mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
- break;
- case TYPE_KEYGUARD:
- mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT);
- break;
- case TYPE_UNFOLD:
- mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
- break;
- default:
- break;
- }
+ @Nullable SurfaceControl.Transaction finishT);
- if (mHasRequestToRemote) {
- mPlayer.getRemoteTransitionHandler().onTransitionConsumed(
- transition, aborted, finishT);
- }
- }
-
- boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info,
+ protected boolean startSubAnimation(
+ Transitions.TransitionHandler handler, TransitionInfo info,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
if (mInfo != null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -573,7 +184,7 @@
return true;
}
- void onSubAnimationFinished(TransitionInfo info, WindowContainerTransaction wct) {
+ private void onSubAnimationFinished(TransitionInfo info, WindowContainerTransaction wct) {
mInFlightSubAnimations--;
if (mInfo != null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -644,7 +255,7 @@
throw new IllegalStateException("Unexpected remote transition in"
+ "pip-enter-from-split request");
}
- mActiveTransitions.add(createMixedTransition(
+ mActiveTransitions.add(createDefaultMixedTransition(
MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition));
WindowContainerTransaction out = new WindowContainerTransaction();
@@ -656,7 +267,7 @@
mActivityEmbeddingController != null)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
" Got a PiP-enter request from an Activity Embedding split");
- mActiveTransitions.add(createMixedTransition(
+ mActiveTransitions.add(createDefaultMixedTransition(
MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
// Postpone transition splitting to later.
WindowContainerTransaction out = new WindowContainerTransaction();
@@ -675,7 +286,7 @@
if (handler == null) {
return null;
}
- final MixedTransition mixed = createMixedTransition(
+ final MixedTransition mixed = createDefaultMixedTransition(
MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition);
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
@@ -701,7 +312,7 @@
mPlayer.getRemoteTransitionHandler(),
new WindowContainerTransaction());
}
- final MixedTransition mixed = createMixedTransition(
+ final MixedTransition mixed = createRecentsMixedTransition(
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
@@ -710,7 +321,7 @@
final WindowContainerTransaction wct =
mUnfoldHandler.handleRequest(transition, request);
if (wct != null) {
- mActiveTransitions.add(createMixedTransition(
+ mActiveTransitions.add(createDefaultMixedTransition(
MixedTransition.TYPE_UNFOLD, transition));
}
return wct;
@@ -718,6 +329,12 @@
return null;
}
+ private DefaultMixedTransition createDefaultMixedTransition(int type, IBinder transition) {
+ return new DefaultMixedTransition(
+ type, transition, mPlayer, this, mPipHandler, mSplitHandler, mKeyguardHandler,
+ mUnfoldHandler, mActivityEmbeddingController);
+ }
+
@Override
public Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT) {
if (mRecentsHandler != null) {
@@ -737,31 +354,30 @@
private void setRecentsTransitionDuringSplit(IBinder transition) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "Split-Screen is foreground, so treat it as Mixed.");
- mActiveTransitions.add(createMixedTransition(
+ mActiveTransitions.add(createRecentsMixedTransition(
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition));
}
private void setRecentsTransitionDuringKeyguard(IBinder transition) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "keyguard is visible, so treat it as Mixed.");
- mActiveTransitions.add(createMixedTransition(
+ mActiveTransitions.add(createRecentsMixedTransition(
MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition));
}
private void setRecentsTransitionDuringDesktop(IBinder transition) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "desktop mode is active, so treat it as Mixed.");
- mActiveTransitions.add(createMixedTransition(
+ mActiveTransitions.add(createRecentsMixedTransition(
MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition));
}
- private MixedTransition createMixedTransition(int type, IBinder transition) {
- return new MixedTransition(type, transition, mPlayer, this, mPipHandler, mRecentsHandler,
- mSplitHandler, mKeyguardHandler, mDesktopTasksController, mUnfoldHandler,
- mActivityEmbeddingController);
+ private MixedTransition createRecentsMixedTransition(int type, IBinder transition) {
+ return new RecentsMixedTransition(type, transition, mPlayer, this, mPipHandler,
+ mSplitHandler, mKeyguardHandler, mRecentsHandler, mDesktopTasksController);
}
- private static TransitionInfo subCopy(@NonNull TransitionInfo info,
+ static TransitionInfo subCopy(@NonNull TransitionInfo info,
@WindowManager.TransitionType int newType, boolean withChanges) {
final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
out.setTrack(info.getTrack());
@@ -778,15 +394,6 @@
return out;
}
- private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
- return change.getTaskInfo() != null
- && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME;
- }
-
- private static boolean isWallpaper(@NonNull TransitionInfo.Change change) {
- return (change.getFlags() & FLAG_IS_WALLPAPER) != 0;
- }
-
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -805,7 +412,7 @@
if (KeyguardTransitionHandler.handles(info)) {
if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) {
final MixedTransition keyguardMixed =
- createMixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
+ createDefaultMixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
mActiveTransitions.add(keyguardMixed);
Transitions.TransitionFinishCallback callback = wct -> {
mActiveTransitions.remove(keyguardMixed);
@@ -845,117 +452,6 @@
return handled;
}
- private static boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull Transitions player, @NonNull DefaultMixedHandler mixedHandler,
- @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
- + "entering PIP while Split-Screen is foreground.");
- TransitionInfo.Change pipChange = null;
- TransitionInfo.Change wallpaper = null;
- final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
- boolean homeIsOpening = false;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- TransitionInfo.Change change = info.getChanges().get(i);
- if (pipHandler.isEnteringPip(change, info.getType())) {
- if (pipChange != null) {
- throw new IllegalStateException("More than 1 pip-entering changes in one"
- + " transition? " + info);
- }
- pipChange = change;
- // going backwards, so remove-by-index is fine.
- everythingElse.getChanges().remove(i);
- } else if (isHomeOpening(change)) {
- homeIsOpening = true;
- } else if (isWallpaper(change)) {
- wallpaper = change;
- }
- }
- if (pipChange == null) {
- // um, something probably went wrong.
- return false;
- }
- final boolean isGoingHome = homeIsOpening;
- Transitions.TransitionFinishCallback finishCB = (wct) -> {
- --mixed.mInFlightSubAnimations;
- mixed.joinFinishArgs(wct);
- if (mixed.mInFlightSubAnimations > 0) return;
- if (isGoingHome) {
- splitHandler.onTransitionAnimationComplete();
- }
- finishCallback.onTransitionFinished(mixed.mFinishWCT);
- };
- if (isGoingHome || splitHandler.getSplitItemPosition(pipChange.getLastParent())
- != SPLIT_POSITION_UNDEFINED) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
- + "since entering-PiP caused us to leave split and return home.");
- // We need to split the transition into 2 parts: the pip part (animated by pip)
- // and the dismiss-part (animated by launcher).
- mixed.mInFlightSubAnimations = 2;
- // immediately make the wallpaper visible (so that we don't see it pop-in during
- // the time it takes to start recents animation (which is remote).
- if (wallpaper != null) {
- startTransaction.show(wallpaper.getLeash()).setAlpha(wallpaper.getLeash(), 1.f);
- }
- // make a new startTransaction because pip's startEnterAnimation "consumes" it so
- // we need a separate one to send over to launcher.
- SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
- @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
- if (splitHandler.isSplitScreenVisible()) {
- // The non-going home case, we could be pip-ing one of the split stages and keep
- // showing the other
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- TransitionInfo.Change change = info.getChanges().get(i);
- if (change == pipChange) {
- // Ignore the change/task that's going into Pip
- continue;
- }
- @SplitScreen.StageType int splitItemStage =
- splitHandler.getSplitItemStage(change.getLastParent());
- if (splitItemStage != STAGE_TYPE_UNDEFINED) {
- topStageToKeep = splitItemStage;
- break;
- }
- }
- }
- // Let split update internal state for dismiss.
- splitHandler.prepareDismissAnimation(topStageToKeep,
- EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
- finishTransaction);
-
- // We are trying to accommodate launcher's close animation which can't handle the
- // divider-bar, so if split-handler is closing the divider-bar, just hide it and remove
- // from transition info.
- for (int i = everythingElse.getChanges().size() - 1; i >= 0; --i) {
- if ((everythingElse.getChanges().get(i).getFlags() & FLAG_IS_DIVIDER_BAR) != 0) {
- everythingElse.getChanges().remove(i);
- break;
- }
- }
-
- pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
- pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
- finishCB);
- // Dispatch the rest of the transition normally. This will most-likely be taken by
- // recents or default handler.
- mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse,
- otherStartT, finishTransaction, finishCB, mixedHandler);
- } else {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just "
- + "forward animation to Pip-Handler.");
- // This happens if the pip-ing activity is in a multi-activity task (and thus a
- // new pip task is spawned). In this case, we don't actually exit split so we can
- // just let pip transition handle the animation verbatim.
- mixed.mInFlightSubAnimations = 1;
- pipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction,
- finishCB);
- }
- return true;
- }
-
private void unlinkMissingParents(TransitionInfo from) {
for (int i = 0; i < from.getChanges().size(); ++i) {
final TransitionInfo.Change chg = from.getChanges().get(i);
@@ -987,15 +483,14 @@
public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
Transitions.TransitionFinishCallback finishCallback) {
- final MixedTransition mixed = createMixedTransition(
+ final MixedTransition mixed = createDefaultMixedTransition(
MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition);
mActiveTransitions.add(mixed);
Transitions.TransitionFinishCallback callback = wct -> {
mActiveTransitions.remove(mixed);
finishCallback.onTransitionFinished(wct);
};
- return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback, mPlayer, this,
- mPipHandler, mSplitHandler);
+ return mixed.startAnimation(transition, info, startT, finishT, callback);
}
/**
@@ -1018,7 +513,7 @@
}
if (displayPart.getChanges().isEmpty()) return false;
unlinkMissingParents(everythingElse);
- final MixedTransition mixed = createMixedTransition(
+ final MixedTransition mixed = createDefaultMixedTransition(
MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition);
mActiveTransitions.add(mixed);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change "
@@ -1135,7 +630,7 @@
* {@link TransitionInfo} so that it can take over some parts of the animation without
* reparenting to new transition roots.
*/
- private static void handoverTransitionLeashes(
+ static void handoverTransitionLeashes(
@NonNull TransitionInfo from,
@NonNull TransitionInfo to,
@NonNull SurfaceControl.Transaction startT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
new file mode 100644
index 0000000..9ce46d6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+
+import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy;
+import static com.android.wm.shell.transition.MixedTransitionHelper.animateEnterPipFromSplit;
+import static com.android.wm.shell.transition.MixedTransitionHelper.animateKeyguard;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
+import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+
+class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
+ private final UnfoldTransitionHandler mUnfoldHandler;
+ private final ActivityEmbeddingController mActivityEmbeddingController;
+
+ DefaultMixedTransition(int type, IBinder transition, Transitions player,
+ DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
+ StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler,
+ UnfoldTransitionHandler unfoldHandler,
+ ActivityEmbeddingController activityEmbeddingController) {
+ super(type, transition, player, mixedHandler, pipHandler, splitHandler, keyguardHandler);
+ mUnfoldHandler = unfoldHandler;
+ mActivityEmbeddingController = activityEmbeddingController;
+
+ switch (type) {
+ case TYPE_UNFOLD:
+ mLeftoversHandler = mUnfoldHandler;
+ break;
+ case TYPE_DISPLAY_AND_SPLIT_CHANGE:
+ case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+ case TYPE_ENTER_PIP_FROM_SPLIT:
+ case TYPE_KEYGUARD:
+ case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+ default:
+ break;
+ }
+ }
+
+ @Override
+ boolean startAnimation(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ return switch (mType) {
+ case TYPE_DISPLAY_AND_SPLIT_CHANGE -> false;
+ case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING ->
+ animateEnterPipFromActivityEmbedding(
+ info, startTransaction, finishTransaction, finishCallback);
+ case TYPE_ENTER_PIP_FROM_SPLIT ->
+ animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
+ finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+ case TYPE_KEYGUARD ->
+ animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback,
+ mKeyguardHandler, mPipHandler);
+ case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE ->
+ animateOpenIntentWithRemoteAndPip(transition, info, startTransaction,
+ finishTransaction, finishCallback);
+ case TYPE_UNFOLD ->
+ animateUnfold(info, startTransaction, finishTransaction, finishCallback);
+ default -> throw new IllegalStateException(
+ "Starting default mixed animation with unknown or illegal type: " + mType);
+ };
+ }
+
+ private boolean animateEnterPipFromActivityEmbedding(
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ + "entering PIP from an Activity Embedding window");
+ // Split into two transitions (wct)
+ TransitionInfo.Change pipChange = null;
+ final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (mPipHandler.isEnteringPip(change, info.getType())) {
+ if (pipChange != null) {
+ throw new IllegalStateException("More than 1 pip-entering changes in one"
+ + " transition? " + info);
+ }
+ pipChange = change;
+ // going backwards, so remove-by-index is fine.
+ everythingElse.getChanges().remove(i);
+ }
+ }
+
+ final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ --mInFlightSubAnimations;
+ joinFinishArgs(wct);
+ if (mInFlightSubAnimations > 0) return;
+ finishCallback.onTransitionFinished(mFinishWCT);
+ };
+
+ if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
+ // Fallback to dispatching to other handlers.
+ return false;
+ }
+
+ // PIP window should always be on the highest Z order.
+ if (pipChange != null) {
+ mInFlightSubAnimations = 2;
+ mPipHandler.startEnterAnimation(
+ pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+ finishTransaction,
+ finishCB);
+ } else {
+ mInFlightSubAnimations = 1;
+ }
+
+ mActivityEmbeddingController.startAnimation(
+ mTransition, everythingElse, startTransaction, finishTransaction, finishCB);
+ return true;
+ }
+
+ private boolean animateOpenIntentWithRemoteAndPip(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ boolean handledToPip = tryAnimateOpenIntentWithRemoteAndPip(
+ info, startTransaction, finishTransaction, finishCallback);
+ // Consume the transition on remote handler if the leftover handler already handle this
+ // transition. And if it cannot, the transition will be handled by remote handler, so don't
+ // consume here.
+ // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip
+ if (handledToPip && mHasRequestToRemote
+ && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null);
+ }
+ return handledToPip;
+ }
+
+ private boolean tryAnimateOpenIntentWithRemoteAndPip(
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ TransitionInfo.Change pipChange = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (mPipHandler.isEnteringPip(change, info.getType())) {
+ if (pipChange != null) {
+ throw new IllegalStateException("More than 1 pip-entering changes in one"
+ + " transition? " + info);
+ }
+ pipChange = change;
+ info.getChanges().remove(i);
+ }
+ }
+ Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ --mInFlightSubAnimations;
+ joinFinishArgs(wct);
+ if (mInFlightSubAnimations > 0) return;
+ finishCallback.onTransitionFinished(mFinishWCT);
+ };
+ if (pipChange == null) {
+ if (mLeftoversHandler != null) {
+ mInFlightSubAnimations = 1;
+ if (mLeftoversHandler.startAnimation(
+ mTransition, info, startTransaction, finishTransaction, finishCB)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
+ + " animation because remote-animation likely doesn't support it");
+ // Split the transition into 2 parts: the pip part and the rest.
+ mInFlightSubAnimations = 2;
+ // make a new startTransaction because pip's startEnterAnimation "consumes" it so
+ // we need a separate one to send over to launcher.
+ SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+
+ mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB);
+
+ // Dispatch the rest of the transition normally.
+ if (mLeftoversHandler != null
+ && mLeftoversHandler.startAnimation(mTransition, info,
+ startTransaction, finishTransaction, finishCB)) {
+ return true;
+ }
+ mLeftoversHandler = mPlayer.dispatchTransition(
+ mTransition, info, startTransaction, finishTransaction, finishCB, mMixedHandler);
+ return true;
+ }
+
+ private boolean animateUnfold(
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ mInFlightSubAnimations--;
+ if (mInFlightSubAnimations > 0) return;
+ finishCallback.onTransitionFinished(wct);
+ };
+ mInFlightSubAnimations = 1;
+ // Sync pip state.
+ if (mPipHandler != null) {
+ mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
+ }
+ if (mSplitHandler != null && mSplitHandler.isSplitActive()) {
+ mSplitHandler.updateSurfaces(startTransaction);
+ }
+ return mUnfoldHandler.startAnimation(
+ mTransition, info, startTransaction, finishTransaction, finishCB);
+ }
+
+ @Override
+ void mergeAnimation(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ switch (mType) {
+ case TYPE_DISPLAY_AND_SPLIT_CHANGE:
+ // queue since no actual animation.
+ return;
+ case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+ mPipHandler.end();
+ mActivityEmbeddingController.mergeAnimation(
+ transition, info, t, mergeTarget, finishCallback);
+ return;
+ case TYPE_ENTER_PIP_FROM_SPLIT:
+ if (mAnimType == ANIM_TYPE_GOING_HOME) {
+ boolean ended = mSplitHandler.end();
+ // If split couldn't end (because it is remote), then don't end everything else
+ // since we have to play out the animation anyways.
+ if (!ended) return;
+ mPipHandler.end();
+ if (mLeftoversHandler != null) {
+ mLeftoversHandler.mergeAnimation(
+ transition, info, t, mergeTarget, finishCallback);
+ }
+ } else {
+ mPipHandler.end();
+ }
+ return;
+ case TYPE_KEYGUARD:
+ mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ return;
+ case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+ mPipHandler.end();
+ if (mLeftoversHandler != null) {
+ mLeftoversHandler.mergeAnimation(
+ transition, info, t, mergeTarget, finishCallback);
+ }
+ return;
+ case TYPE_UNFOLD:
+ mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ return;
+ default:
+ throw new IllegalStateException("Playing a default mixed transition with unknown or"
+ + " illegal type: " + mType);
+ }
+ }
+
+ @Override
+ void onTransitionConsumed(
+ @NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
+ switch (mType) {
+ case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+ mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
+ break;
+ case TYPE_ENTER_PIP_FROM_SPLIT:
+ mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ break;
+ case TYPE_KEYGUARD:
+ mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT);
+ break;
+ case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE:
+ mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
+ break;
+ case TYPE_UNFOLD:
+ mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
+ break;
+ default:
+ break;
+ }
+
+ if (mHasRequestToRemote) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
new file mode 100644
index 0000000..0974cd1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
+import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy;
+
+import android.annotation.NonNull;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+
+public class MixedTransitionHelper {
+ static boolean animateEnterPipFromSplit(
+ @NonNull DefaultMixedHandler.MixedTransition mixed, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull Transitions player, @NonNull DefaultMixedHandler mixedHandler,
+ @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ + "entering PIP while Split-Screen is foreground.");
+ TransitionInfo.Change pipChange = null;
+ TransitionInfo.Change wallpaper = null;
+ final TransitionInfo everythingElse =
+ subCopy(info, TRANSIT_TO_BACK, true /* changes */);
+ boolean homeIsOpening = false;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (pipHandler.isEnteringPip(change, info.getType())) {
+ if (pipChange != null) {
+ throw new IllegalStateException("More than 1 pip-entering changes in one"
+ + " transition? " + info);
+ }
+ pipChange = change;
+ // going backwards, so remove-by-index is fine.
+ everythingElse.getChanges().remove(i);
+ } else if (isHomeOpening(change)) {
+ homeIsOpening = true;
+ } else if (isWallpaper(change)) {
+ wallpaper = change;
+ }
+ }
+ if (pipChange == null) {
+ // um, something probably went wrong.
+ return false;
+ }
+ final boolean isGoingHome = homeIsOpening;
+ Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ --mixed.mInFlightSubAnimations;
+ mixed.joinFinishArgs(wct);
+ if (mixed.mInFlightSubAnimations > 0) return;
+ if (isGoingHome) {
+ splitHandler.onTransitionAnimationComplete();
+ }
+ finishCallback.onTransitionFinished(mixed.mFinishWCT);
+ };
+ if (isGoingHome || splitHandler.getSplitItemPosition(pipChange.getLastParent())
+ != SPLIT_POSITION_UNDEFINED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
+ + "since entering-PiP caused us to leave split and return home.");
+ // We need to split the transition into 2 parts: the pip part (animated by pip)
+ // and the dismiss-part (animated by launcher).
+ mixed.mInFlightSubAnimations = 2;
+ // immediately make the wallpaper visible (so that we don't see it pop-in during
+ // the time it takes to start recents animation (which is remote).
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash()).setAlpha(wallpaper.getLeash(), 1.f);
+ }
+ // make a new startTransaction because pip's startEnterAnimation "consumes" it so
+ // we need a separate one to send over to launcher.
+ SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+ @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
+ if (splitHandler.isSplitScreenVisible()) {
+ // The non-going home case, we could be pip-ing one of the split stages and keep
+ // showing the other
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == pipChange) {
+ // Ignore the change/task that's going into Pip
+ continue;
+ }
+ @SplitScreen.StageType int splitItemStage =
+ splitHandler.getSplitItemStage(change.getLastParent());
+ if (splitItemStage != STAGE_TYPE_UNDEFINED) {
+ topStageToKeep = splitItemStage;
+ break;
+ }
+ }
+ }
+ // Let split update internal state for dismiss.
+ splitHandler.prepareDismissAnimation(topStageToKeep,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
+ finishTransaction);
+
+ // We are trying to accommodate launcher's close animation which can't handle the
+ // divider-bar, so if split-handler is closing the divider-bar, just hide it and
+ // remove from transition info.
+ for (int i = everythingElse.getChanges().size() - 1; i >= 0; --i) {
+ if ((everythingElse.getChanges().get(i).getFlags() & FLAG_IS_DIVIDER_BAR)
+ != 0) {
+ everythingElse.getChanges().remove(i);
+ break;
+ }
+ }
+
+ pipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
+ pipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
+ finishCB);
+ // Dispatch the rest of the transition normally. This will most-likely be taken by
+ // recents or default handler.
+ mixed.mLeftoversHandler = player.dispatchTransition(mixed.mTransition, everythingElse,
+ otherStartT, finishTransaction, finishCB, mixedHandler);
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just "
+ + "forward animation to Pip-Handler.");
+ // This happens if the pip-ing activity is in a multi-activity task (and thus a
+ // new pip task is spawned). In this case, we don't actually exit split so we can
+ // just let pip transition handle the animation verbatim.
+ mixed.mInFlightSubAnimations = 1;
+ pipHandler.startAnimation(
+ mixed.mTransition, info, startTransaction, finishTransaction, finishCB);
+ }
+ return true;
+ }
+
+ private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
+ return change.getTaskInfo() != null
+ && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME;
+ }
+
+ private static boolean isWallpaper(@NonNull TransitionInfo.Change change) {
+ return (change.getFlags() & FLAG_IS_WALLPAPER) != 0;
+ }
+
+ static boolean animateKeyguard(
+ @NonNull DefaultMixedHandler.MixedTransition mixed, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull KeyguardTransitionHandler keyguardHandler,
+ PipTransitionController pipHandler) {
+ if (mixed.mFinishT == null) {
+ mixed.mFinishT = finishTransaction;
+ mixed.mFinishCB = finishCallback;
+ }
+ // Sync pip state.
+ if (pipHandler != null) {
+ pipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
+ }
+ return mixed.startSubAnimation(keyguardHandler, info, startTransaction, finishTransaction);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
new file mode 100644
index 0000000..643e026
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.transition.DefaultMixedHandler.handoverTransitionLeashes;
+import static com.android.wm.shell.transition.MixedTransitionHelper.animateEnterPipFromSplit;
+import static com.android.wm.shell.transition.MixedTransitionHelper.animateKeyguard;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+
+class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
+ private final RecentsTransitionHandler mRecentsHandler;
+ private final DesktopTasksController mDesktopTasksController;
+
+ RecentsMixedTransition(int type, IBinder transition, Transitions player,
+ DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
+ StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler,
+ RecentsTransitionHandler recentsHandler,
+ DesktopTasksController desktopTasksController) {
+ super(type, transition, player, mixedHandler, pipHandler, splitHandler, keyguardHandler);
+ mRecentsHandler = recentsHandler;
+ mDesktopTasksController = desktopTasksController;
+ mLeftoversHandler = mRecentsHandler;
+ }
+
+ @Override
+ boolean startAnimation(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ return switch (mType) {
+ case TYPE_RECENTS_DURING_DESKTOP ->
+ animateRecentsDuringDesktop(
+ info, startTransaction, finishTransaction, finishCallback);
+ case TYPE_RECENTS_DURING_KEYGUARD ->
+ animateRecentsDuringKeyguard(
+ info, startTransaction, finishTransaction, finishCallback);
+ case TYPE_RECENTS_DURING_SPLIT ->
+ animateRecentsDuringSplit(
+ info, startTransaction, finishTransaction, finishCallback);
+ default -> throw new IllegalStateException(
+ "Starting Recents mixed animation with unknown or illegal type: " + mType);
+ };
+ }
+
+ private boolean animateRecentsDuringDesktop(
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mInfo == null) {
+ mInfo = info;
+ mFinishT = finishTransaction;
+ mFinishCB = finishCallback;
+ }
+ Transitions.TransitionFinishCallback finishCB = wct -> {
+ mInFlightSubAnimations--;
+ if (mInFlightSubAnimations == 0) {
+ finishCallback.onTransitionFinished(wct);
+ }
+ };
+
+ mInFlightSubAnimations++;
+ boolean consumed = mRecentsHandler.startAnimation(
+ mTransition, info, startTransaction, finishTransaction, finishCB);
+ if (!consumed) {
+ mInFlightSubAnimations--;
+ return false;
+ }
+ if (mDesktopTasksController != null) {
+ mDesktopTasksController.syncSurfaceState(info, finishTransaction);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean animateRecentsDuringKeyguard(
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mInfo == null) {
+ mInfo = info;
+ mFinishT = finishTransaction;
+ mFinishCB = finishCallback;
+ }
+ return startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction);
+ }
+
+ private boolean animateRecentsDuringSplit(
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ // Pip auto-entering info might be appended to recent transition like pressing
+ // home-key in 3-button navigation. This offers split handler the opportunity to
+ // handle split to pip animation.
+ if (mPipHandler.isEnteringPip(change, info.getType())
+ && mSplitHandler.getSplitItemPosition(change.getLastParent())
+ != SPLIT_POSITION_UNDEFINED) {
+ return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
+ finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+ }
+ }
+
+ // Split-screen is only interested in the recents transition finishing (and merging), so
+ // just wrap finish and start recents animation directly.
+ Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ mInFlightSubAnimations = 0;
+ // If pair-to-pair switching, the post-recents clean-up isn't needed.
+ wct = wct != null ? wct : new WindowContainerTransaction();
+ if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) {
+ mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
+ } else {
+ // notify pair-to-pair recents animation finish
+ mSplitHandler.onRecentsPairToPairAnimationFinish(wct);
+ }
+ mSplitHandler.onTransitionAnimationComplete();
+ finishCallback.onTransitionFinished(wct);
+ };
+ mInFlightSubAnimations = 1;
+ mSplitHandler.onRecentsInSplitAnimationStart(info);
+ final boolean handled = mLeftoversHandler.startAnimation(
+ mTransition, info, startTransaction, finishTransaction, finishCB);
+ if (!handled) {
+ mSplitHandler.onRecentsInSplitAnimationCanceled();
+ }
+ return handled;
+ }
+
+ @Override
+ void mergeAnimation(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ switch (mType) {
+ case TYPE_RECENTS_DURING_DESKTOP:
+ mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ return;
+ case TYPE_RECENTS_DURING_KEYGUARD:
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
+ handoverTransitionLeashes(mInfo, info, t, mFinishT);
+ if (animateKeyguard(
+ this, info, t, mFinishT, mFinishCB, mKeyguardHandler, mPipHandler)) {
+ finishCallback.onTransitionFinished(null);
+ }
+ }
+ mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
+ return;
+ case TYPE_RECENTS_DURING_SPLIT:
+ if (mSplitHandler.isPendingEnter(transition)) {
+ // Recents -> enter-split means that we are switching from one pair to
+ // another pair.
+ mAnimType = DefaultMixedHandler.MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
+ }
+ mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ return;
+ default:
+ throw new IllegalStateException("Playing a Recents mixed transition with unknown or"
+ + " illegal type: " + mType);
+ }
+ }
+
+ @Override
+ void onTransitionConsumed(
+ @NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
+ switch (mType) {
+ case TYPE_RECENTS_DURING_DESKTOP:
+ case TYPE_RECENTS_DURING_SPLIT:
+ mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
+ break;
+ default:
+ break;
+ }
+
+ if (mHasRequestToRemote) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
+ }
+ }
+}