Merge "Add a DefaultMixedHandler handler combined transitions" into tm-qpr-dev am: f7369c8bc4
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17621381 Change-Id: Ied412a43b6513cdb4138eec7257828824ba878d2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.startingsurface.StartingWindowController;
|
||||
import com.android.wm.shell.transition.DefaultMixedHandler;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
|
||||
@@ -131,6 +132,13 @@ public class ShellInitImpl {
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||
mTransitions.register(mShellTaskOrganizer);
|
||||
mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init);
|
||||
if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) {
|
||||
final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions,
|
||||
mPipTouchHandlerOptional.get().getTransitionHandler(),
|
||||
mSplitScreenOptional.get().getTransitionHandler());
|
||||
// Added at end so that it has highest priority.
|
||||
mTransitions.addHandler(mixedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/181599115): This should really be the pip controller, but until we can provide the
|
||||
|
||||
@@ -295,6 +295,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
|
||||
displayController.addDisplayWindowListener(this);
|
||||
}
|
||||
|
||||
public PipTransitionController getTransitionController() {
|
||||
return mPipTransitionController;
|
||||
}
|
||||
|
||||
public Rect getCurrentOrAnimatingBounds() {
|
||||
PipAnimationController.PipTransitionAnimator animator =
|
||||
mPipAnimationController.getCurrentAnimator();
|
||||
|
||||
@@ -28,7 +28,6 @@ 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;
|
||||
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
|
||||
@@ -52,6 +51,7 @@ import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
@@ -217,8 +217,9 @@ public class PipTransition extends PipTransitionController {
|
||||
}
|
||||
|
||||
// Entering PIP.
|
||||
if (isEnteringPip(info, mCurrentPipTaskToken)) {
|
||||
return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
|
||||
if (isEnteringPip(info)) {
|
||||
startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
// For transition that we don't animate, but contains the PIP leash, we need to update the
|
||||
@@ -245,22 +246,31 @@ public class PipTransition extends PipTransitionController {
|
||||
@Override
|
||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||
@NonNull TransitionRequestInfo request) {
|
||||
if (request.getType() == TRANSIT_PIP) {
|
||||
if (requestHasPipEnter(request)) {
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
mRequestedEnterTransition = transition;
|
||||
mRequestedEnterTask = request.getTriggerTask().token;
|
||||
wct.setActivityWindowingMode(request.getTriggerTask().token,
|
||||
WINDOWING_MODE_UNDEFINED);
|
||||
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
||||
wct.setBounds(request.getTriggerTask().token, destinationBounds);
|
||||
}
|
||||
augmentRequest(transition, request, wct);
|
||||
return wct;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void augmentRequest(@NonNull IBinder transition,
|
||||
@NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
|
||||
if (!requestHasPipEnter(request)) {
|
||||
throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
|
||||
}
|
||||
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
mRequestedEnterTransition = transition;
|
||||
mRequestedEnterTask = request.getTriggerTask().token;
|
||||
outWCT.setActivityWindowingMode(request.getTriggerTask().token,
|
||||
WINDOWING_MODE_UNDEFINED);
|
||||
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
||||
outWCT.setBounds(request.getTriggerTask().token, destinationBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleRotateDisplay(int startRotation, int endRotation,
|
||||
WindowContainerTransaction wct) {
|
||||
@@ -559,92 +569,94 @@ public class PipTransition extends PipTransitionController {
|
||||
}
|
||||
|
||||
/** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
|
||||
private static boolean isEnteringPip(@NonNull TransitionInfo info,
|
||||
@Nullable WindowContainerToken currentPipTaskToken) {
|
||||
private boolean isEnteringPip(@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
|
||||
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
|
||||
&& !change.getContainer().equals(currentPipTaskToken)) {
|
||||
// We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
|
||||
// that enter PiP instantly on opening, mostly from CTS/Flicker tests)
|
||||
if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) {
|
||||
return true;
|
||||
}
|
||||
// This can happen if the request to enter PIP happens when we are collecting for
|
||||
// another transition, such as TRANSIT_CHANGE (display rotation).
|
||||
if (info.getType() == TRANSIT_CHANGE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Please file a bug to handle the unexpected transition type.
|
||||
throw new IllegalStateException("Entering PIP with unexpected transition type="
|
||||
+ transitTypeToString(info.getType()));
|
||||
}
|
||||
if (isEnteringPip(change, info.getType())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean startEnterAnimation(@NonNull TransitionInfo info,
|
||||
/** Whether a particular change is a window that is entering pip. */
|
||||
@Override
|
||||
public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
|
||||
@WindowManager.TransitionType int transitType) {
|
||||
if (change.getTaskInfo() != null
|
||||
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
|
||||
&& !change.getContainer().equals(mCurrentPipTaskToken)) {
|
||||
// We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
|
||||
// that enter PiP instantly on opening, mostly from CTS/Flicker tests)
|
||||
if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN) {
|
||||
return true;
|
||||
}
|
||||
// This can happen if the request to enter PIP happens when we are collecting for
|
||||
// another transition, such as TRANSIT_CHANGE (display rotation).
|
||||
if (transitType == TRANSIT_CHANGE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Please file a bug to handle the unexpected transition type.
|
||||
throw new IllegalStateException("Entering PIP with unexpected transition type="
|
||||
+ transitTypeToString(transitType));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void startEnterAnimation(@NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
// Search for an Enter PiP transition (along with a show wallpaper one)
|
||||
// Search for an Enter PiP transition
|
||||
TransitionInfo.Change enterPip = null;
|
||||
TransitionInfo.Change wallpaper = null;
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (change.getTaskInfo() != null
|
||||
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
|
||||
enterPip = change;
|
||||
} else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
|
||||
wallpaper = change;
|
||||
}
|
||||
}
|
||||
if (enterPip == null) {
|
||||
return false;
|
||||
}
|
||||
// Keep track of the PIP task.
|
||||
mCurrentPipTaskToken = enterPip.getContainer();
|
||||
mHasFadeOut = false;
|
||||
|
||||
if (mFinishCallback != null) {
|
||||
callFinishCallback(null /* wct */);
|
||||
mFinishTransaction = null;
|
||||
throw new RuntimeException("Previous callback not called, aborting entering PIP.");
|
||||
throw new IllegalStateException("Trying to start PiP animation without a pip"
|
||||
+ "participant");
|
||||
}
|
||||
|
||||
// Show the wallpaper if there is a wallpaper change.
|
||||
if (wallpaper != null) {
|
||||
startTransaction.show(wallpaper.getLeash());
|
||||
startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
|
||||
}
|
||||
// Make sure other open changes are visible as entering PIP. Some may be hidden in
|
||||
// Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (change == enterPip || change == wallpaper) {
|
||||
continue;
|
||||
}
|
||||
if (change == enterPip) continue;
|
||||
if (isOpeningType(change.getMode())) {
|
||||
final SurfaceControl leash = change.getLeash();
|
||||
startTransaction.show(leash).setAlpha(leash, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
startEnterAnimation(enterPip, startTransaction, finishTransaction, finishCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
|
||||
@NonNull final SurfaceControl.Transaction startTransaction,
|
||||
@NonNull final SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull final Transitions.TransitionFinishCallback finishCallback) {
|
||||
if (mFinishCallback != null) {
|
||||
callFinishCallback(null /* wct */);
|
||||
mFinishTransaction = null;
|
||||
throw new RuntimeException("Previous callback not called, aborting entering PIP.");
|
||||
}
|
||||
|
||||
// Keep track of the PIP task and animation.
|
||||
mCurrentPipTaskToken = pipChange.getContainer();
|
||||
mHasFadeOut = false;
|
||||
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
|
||||
mFinishCallback = finishCallback;
|
||||
mFinishTransaction = finishTransaction;
|
||||
final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
|
||||
return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
|
||||
startTransaction, finishTransaction, enterPip.getStartRotation(),
|
||||
endRotation);
|
||||
}
|
||||
|
||||
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
|
||||
final SurfaceControl.Transaction startTransaction,
|
||||
final SurfaceControl.Transaction finishTransaction,
|
||||
final int startRotation, final int endRotation) {
|
||||
final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo();
|
||||
final SurfaceControl leash = pipChange.getLeash();
|
||||
final int startRotation = pipChange.getStartRotation();
|
||||
final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation();
|
||||
|
||||
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
|
||||
taskInfo.topActivityInfo);
|
||||
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
||||
@@ -657,7 +669,6 @@ public class PipTransition extends PipTransitionController {
|
||||
computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
|
||||
destinationBounds, sourceHintRect);
|
||||
}
|
||||
PipAnimationController.PipTransitionAnimator animator;
|
||||
// Set corner radius for entering pip.
|
||||
mSurfaceTransactionHelper
|
||||
.crop(finishTransaction, leash, destinationBounds)
|
||||
@@ -694,7 +705,7 @@ public class PipTransition extends PipTransitionController {
|
||||
null /* callback */, false /* withStartDelay */);
|
||||
}
|
||||
mPipTransitionState.setInSwipePipToHomeTransition(false);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotationDelta != Surface.ROTATION_0) {
|
||||
@@ -702,6 +713,12 @@ public class PipTransition extends PipTransitionController {
|
||||
tmpTransform.postRotate(rotationDelta);
|
||||
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
|
||||
}
|
||||
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
startTransaction.setAlpha(leash, 0f);
|
||||
}
|
||||
startTransaction.apply();
|
||||
|
||||
PipAnimationController.PipTransitionAnimator animator;
|
||||
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
|
||||
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
|
||||
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
|
||||
@@ -712,7 +729,6 @@ public class PipTransition extends PipTransitionController {
|
||||
animator.setUseContentOverlay(mContext);
|
||||
}
|
||||
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
startTransaction.setAlpha(leash, 0f);
|
||||
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
|
||||
0f, 1f);
|
||||
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
|
||||
@@ -720,7 +736,6 @@ public class PipTransition extends PipTransitionController {
|
||||
throw new RuntimeException("Unrecognized animation type: "
|
||||
+ mOneShotAnimationType);
|
||||
}
|
||||
startTransaction.apply();
|
||||
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(mEnterExitAnimationDuration);
|
||||
@@ -731,8 +746,6 @@ public class PipTransition extends PipTransitionController {
|
||||
animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
|
||||
}
|
||||
animator.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Computes destination bounds in old rotation and updates source hint rect if available. */
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.wm.shell.pip;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.view.WindowManager.TRANSIT_PIP;
|
||||
|
||||
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
|
||||
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
|
||||
@@ -28,10 +29,16 @@ import android.content.ComponentName;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
@@ -206,6 +213,30 @@ public abstract class PipTransitionController implements Transitions.TransitionH
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return whether the transition-request represents a pip-entry. */
|
||||
public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) {
|
||||
return request.getType() == TRANSIT_PIP;
|
||||
}
|
||||
|
||||
/** Whether a particular change is a window that is entering pip. */
|
||||
public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
|
||||
@WindowManager.TransitionType int transitType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Add PiP-related changes to `outWCT` for the given request. */
|
||||
public void augmentRequest(@NonNull IBinder transition,
|
||||
@NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
|
||||
throw new IllegalStateException("Request isn't entering PiP");
|
||||
}
|
||||
|
||||
/** Play a transition animation for entering PiP on a specific PiP change. */
|
||||
public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
|
||||
@NonNull final SurfaceControl.Transaction startTransaction,
|
||||
@NonNull final SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull final Transitions.TransitionFinishCallback finishCallback) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface for PiP transitions (both from and to PiP mode)
|
||||
*/
|
||||
|
||||
@@ -53,6 +53,7 @@ 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.PipTaskOrganizer;
|
||||
import com.android.wm.shell.pip.PipTransitionController;
|
||||
import com.android.wm.shell.pip.PipUiEventLogger;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
|
||||
@@ -249,6 +250,10 @@ public class PipTouchHandler {
|
||||
});
|
||||
}
|
||||
|
||||
public PipTransitionController getTransitionHandler() {
|
||||
return mPipTaskOrganizer.getTransitionController();
|
||||
}
|
||||
|
||||
private void reloadResources() {
|
||||
final Resources res = mContext.getResources();
|
||||
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
|
||||
|
||||
@@ -108,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
|
||||
static final int EXIT_REASON_SCREEN_LOCKED = 7;
|
||||
static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
|
||||
static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
|
||||
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
|
||||
@IntDef(value = {
|
||||
EXIT_REASON_UNKNOWN,
|
||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
|
||||
@@ -198,6 +198,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
return mStageCoordinator.isSplitScreenVisible();
|
||||
}
|
||||
|
||||
public StageCoordinator getTransitionHandler() {
|
||||
return mStageCoordinator;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
|
||||
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
||||
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
||||
@@ -58,9 +57,6 @@ import java.util.ArrayList;
|
||||
class SplitScreenTransitions {
|
||||
private static final String TAG = "SplitScreenTransitions";
|
||||
|
||||
/** Flag applied to a transition change to identify it as a divider bar for animation. */
|
||||
public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
|
||||
|
||||
private final TransactionPool mTransactionPool;
|
||||
private final Transitions mTransitions;
|
||||
private final Runnable mOnFinish;
|
||||
@@ -187,27 +183,28 @@ class SplitScreenTransitions {
|
||||
}
|
||||
|
||||
/** Starts a transition to dismiss split. */
|
||||
IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
|
||||
IBinder startDismissTransition(WindowContainerTransaction wct,
|
||||
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
|
||||
@SplitScreenController.ExitReason int reason) {
|
||||
final int type = reason == EXIT_REASON_DRAG_DIVIDER
|
||||
? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
|
||||
if (transition == null) {
|
||||
transition = mTransitions.startTransition(type, wct, handler);
|
||||
}
|
||||
IBinder transition = mTransitions.startTransition(type, wct, handler);
|
||||
setDismissTransition(transition, dismissTop, reason);
|
||||
return transition;
|
||||
}
|
||||
|
||||
/** Sets a transition to dismiss split. */
|
||||
void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop,
|
||||
@SplitScreenController.ExitReason int reason) {
|
||||
mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
|
||||
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Dismiss due to %s. toTop=%s",
|
||||
exitReasonToString(reason), stageTypeToString(dismissTop));
|
||||
return transition;
|
||||
}
|
||||
|
||||
IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
|
||||
Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) {
|
||||
if (transition == null) {
|
||||
transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler);
|
||||
}
|
||||
void setRecentTransition(@NonNull IBinder transition,
|
||||
@Nullable RemoteTransition remoteTransition) {
|
||||
mPendingRecent = transition;
|
||||
|
||||
if (remoteTransition != null) {
|
||||
@@ -219,7 +216,6 @@ class SplitScreenTransitions {
|
||||
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Enter recent panel");
|
||||
return transition;
|
||||
}
|
||||
|
||||
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
|
||||
|
||||
@@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.view.WindowManager.transitTypeToString;
|
||||
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
||||
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
|
||||
@@ -47,7 +48,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
|
||||
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
|
||||
@@ -126,12 +126,15 @@ import javax.inject.Provider;
|
||||
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
|
||||
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
|
||||
*/
|
||||
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler,
|
||||
ShellTaskOrganizer.TaskListener {
|
||||
|
||||
private static final String TAG = StageCoordinator.class.getSimpleName();
|
||||
|
||||
/** Flag applied to a transition change to identify it as a divider bar for animation. */
|
||||
public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
|
||||
|
||||
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
private final MainStage mMainStage;
|
||||
@@ -619,7 +622,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
|
||||
mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
|
||||
mSplitTransitions.startDismissTransition(wct, this,
|
||||
mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
|
||||
} else {
|
||||
exitSplitScreen(
|
||||
@@ -649,8 +652,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(dismissTop, wct);
|
||||
mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
|
||||
dismissTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
|
||||
mSplitTransitions.startDismissTransition(wct, this, dismissTop,
|
||||
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -712,10 +715,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
.setWindowCrop(mSideStage.mRootLeash, null);
|
||||
});
|
||||
|
||||
// Hide divider and reset its position.
|
||||
mSplitLayout.resetDividerPosition();
|
||||
mSplitLayout.release();
|
||||
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
|
||||
onTransitionAnimationComplete();
|
||||
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
|
||||
// Log the exit
|
||||
if (childrenToTop != null) {
|
||||
@@ -1111,8 +1111,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(dismissTop, wct);
|
||||
mSplitTransitions.startDismissTransition(
|
||||
null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
|
||||
mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1301,7 +1300,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
@Nullable TransitionRequestInfo request) {
|
||||
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
|
||||
if (triggerTask == null) {
|
||||
if (mMainStage.isActive()) {
|
||||
if (isSplitActive()) {
|
||||
// Check if the display is rotating.
|
||||
final TransitionRequestInfo.DisplayChange displayChange =
|
||||
request.getDisplayChange();
|
||||
if (request.getType() == TRANSIT_CHANGE && displayChange != null
|
||||
@@ -1328,7 +1328,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
|
||||
}
|
||||
|
||||
if (mMainStage.isActive()) {
|
||||
if (isSplitActive()) {
|
||||
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
|
||||
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
|
||||
@@ -1343,7 +1343,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
|
||||
: STAGE_TYPE_MAIN;
|
||||
prepareExitSplitScreen(dismissTop, out);
|
||||
mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
|
||||
mSplitTransitions.setDismissTransition(transition, dismissTop,
|
||||
EXIT_REASON_APP_FINISHED);
|
||||
}
|
||||
} else if (isOpening && inFullscreen) {
|
||||
@@ -1353,12 +1353,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
} else if (activityType == ACTIVITY_TYPE_HOME
|
||||
|| activityType == ACTIVITY_TYPE_RECENTS) {
|
||||
// Enter overview panel, so start recent transition.
|
||||
mSplitTransitions.startRecentTransition(transition, out, this,
|
||||
mSplitTransitions.setRecentTransition(transition,
|
||||
request.getRemoteTransition());
|
||||
} else {
|
||||
// Occluded by the other fullscreen task, so dismiss both.
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
|
||||
mSplitTransitions.startDismissTransition(transition, out, this,
|
||||
mSplitTransitions.setDismissTransition(transition,
|
||||
STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
|
||||
}
|
||||
}
|
||||
@@ -1373,6 +1373,33 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used for mixed scenarios. For such scenarios, just make sure to include exiting
|
||||
* split or entering split when appropriate.
|
||||
*/
|
||||
public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
|
||||
@NonNull WindowContainerTransaction outWCT) {
|
||||
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
|
||||
if (triggerTask != null && triggerTask.displayId != mDisplayId) {
|
||||
// Skip handling task on the other display.
|
||||
return;
|
||||
}
|
||||
final @WindowManager.TransitionType int type = request.getType();
|
||||
if (isSplitActive() && !isOpeningType(type)
|
||||
&& (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " One of the splits became "
|
||||
+ "empty during a mixed transition (one not handled by split),"
|
||||
+ " so make sure split-screen state is cleaned-up. "
|
||||
+ "mainStageCount=%d sideStageCount=%d", mMainStage.getChildCount(),
|
||||
mSideStage.getChildCount());
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSplitActive() {
|
||||
return mMainStage.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||
@@ -1465,7 +1492,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
return true;
|
||||
}
|
||||
|
||||
void onTransitionAnimationComplete() {
|
||||
/** Called to clean-up state and do house-keeping after the animation is done. */
|
||||
public void onTransitionAnimationComplete() {
|
||||
// If still playing, let it finish.
|
||||
if (!mMainStage.isActive()) {
|
||||
// Update divider state after animation so that it is still around and positioned
|
||||
@@ -1529,8 +1557,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean startPendingDismissAnimation(
|
||||
@NonNull SplitScreenTransitions.DismissTransition dismissTransition,
|
||||
/** Synchronize split-screen state with transition and make appropriate preparations. */
|
||||
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
|
||||
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
|
||||
@NonNull SurfaceControl.Transaction finishT) {
|
||||
// Make some noise if things aren't totally expected. These states shouldn't effect
|
||||
@@ -1563,7 +1591,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mRecentTasks.ifPresent(recentTasks -> {
|
||||
// Notify recents if we are exiting in a way that breaks the pair, and disable further
|
||||
// updates to splits in the recents until we enter split again
|
||||
if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
|
||||
if (shouldBreakPairedTaskInRecents(dismissReason) && mShouldUpdateRecents) {
|
||||
for (TransitionInfo.Change change : info.getChanges()) {
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
if (taskInfo != null
|
||||
@@ -1580,30 +1608,38 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
// Wait until after animation to update divider
|
||||
|
||||
// Reset crops so they don't interfere with subsequent launches
|
||||
t.setWindowCrop(mMainStage.mRootLeash, null);
|
||||
t.setWindowCrop(mSideStage.mRootLeash, null);
|
||||
t.setCrop(mMainStage.mRootLeash, null);
|
||||
t.setCrop(mSideStage.mRootLeash, null);
|
||||
|
||||
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
|
||||
logExit(dismissTransition.mReason);
|
||||
// TODO: Have a proper remote for this. Until then, though, reset state and use the
|
||||
// normal animation stuff (which falls back to the normal launcher remote).
|
||||
mSplitLayout.release(t);
|
||||
mSplitTransitions.mPendingDismiss = null;
|
||||
return false;
|
||||
if (toStage == STAGE_TYPE_UNDEFINED) {
|
||||
logExit(dismissReason);
|
||||
} else {
|
||||
logExitToStage(dismissTransition.mReason,
|
||||
dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
|
||||
logExitToStage(dismissReason, toStage == STAGE_TYPE_MAIN);
|
||||
}
|
||||
|
||||
addDividerBarToTransition(info, t, false /* show */);
|
||||
// We're dismissing split by moving the other one to fullscreen.
|
||||
// Since we don't have any animations for this yet, just use the internal example
|
||||
// animations.
|
||||
|
||||
// Hide divider and dim layer on transition finished.
|
||||
setDividerVisibility(false, finishT);
|
||||
finishT.hide(mMainStage.mDimLayer);
|
||||
finishT.hide(mSideStage.mDimLayer);
|
||||
}
|
||||
|
||||
private boolean startPendingDismissAnimation(
|
||||
@NonNull SplitScreenTransitions.DismissTransition dismissTransition,
|
||||
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
|
||||
@NonNull SurfaceControl.Transaction finishT) {
|
||||
prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
|
||||
t, finishT);
|
||||
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
|
||||
// TODO: Have a proper remote for this. Until then, though, reset state and use the
|
||||
// normal animation stuff (which falls back to the normal launcher remote).
|
||||
t.hide(mSplitLayout.getDividerLeash());
|
||||
mSplitLayout.release(t);
|
||||
mSplitTransitions.mPendingDismiss = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1776,7 +1812,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
||||
mSplitTransitions.startDismissTransition(null /* transition */, wct,
|
||||
mSplitTransitions.startDismissTransition(wct,
|
||||
StageCoordinator.this, STAGE_TYPE_UNDEFINED,
|
||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
|
||||
}
|
||||
|
||||
@@ -268,13 +268,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
mChildrenTaskInfo.remove(taskId);
|
||||
mChildrenLeashes.remove(taskId);
|
||||
mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
|
||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
|
||||
mCallbacks.onChildTaskEnterPip(taskId);
|
||||
}
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
// Status is managed/synchronized by the transition lifecycle.
|
||||
return;
|
||||
}
|
||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
|
||||
mCallbacks.onChildTaskEnterPip(taskId);
|
||||
}
|
||||
sendStatusChanged();
|
||||
} else {
|
||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.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.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.pip.PipTransitionController;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.splitscreen.StageCoordinator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A handler for dealing with transitions involving multiple other handlers. For example: an
|
||||
* activity in split-screen going into PiP.
|
||||
*/
|
||||
public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
|
||||
private final Transitions mPlayer;
|
||||
private final PipTransitionController mPipHandler;
|
||||
private final StageCoordinator mSplitHandler;
|
||||
|
||||
private static class MixedTransition {
|
||||
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
|
||||
|
||||
final int mType;
|
||||
final IBinder mTransition;
|
||||
|
||||
Transitions.TransitionFinishCallback mFinishCallback = null;
|
||||
|
||||
/**
|
||||
* Mixed transitions are made up of multiple "parts". This keeps track of how many
|
||||
* parts are currently animating.
|
||||
*/
|
||||
int mInFlightSubAnimations = 0;
|
||||
|
||||
MixedTransition(int type, IBinder transition) {
|
||||
mType = type;
|
||||
mTransition = transition;
|
||||
}
|
||||
}
|
||||
private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
|
||||
|
||||
public DefaultMixedHandler(@NonNull Transitions player,
|
||||
@NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
|
||||
mPlayer = player;
|
||||
mPipHandler = pipHandler;
|
||||
mSplitHandler = splitHandler;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||
@NonNull TransitionRequestInfo request) {
|
||||
if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while "
|
||||
+ "Split-Screen is active, so treat it as Mixed.");
|
||||
if (request.getRemoteTransition() != null) {
|
||||
throw new IllegalStateException("Unexpected remote transition in"
|
||||
+ "pip-enter-from-split request");
|
||||
}
|
||||
mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT,
|
||||
transition));
|
||||
|
||||
WindowContainerTransaction out = new WindowContainerTransaction();
|
||||
mPipHandler.augmentRequest(transition, request, out);
|
||||
mSplitHandler.addEnterOrExitIfNeeded(request, out);
|
||||
return out;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private TransitionInfo subCopy(@NonNull TransitionInfo info,
|
||||
@WindowManager.TransitionType int newType) {
|
||||
final TransitionInfo out = new TransitionInfo(newType, info.getFlags());
|
||||
for (int i = 0; i < info.getChanges().size(); ++i) {
|
||||
out.getChanges().add(info.getChanges().get(i));
|
||||
}
|
||||
out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y);
|
||||
out.setAnimationOptions(info.getAnimationOptions());
|
||||
return out;
|
||||
}
|
||||
|
||||
private boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
|
||||
return change.getTaskInfo() != null
|
||||
&& change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME;
|
||||
}
|
||||
|
||||
private 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,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
MixedTransition mixed = null;
|
||||
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
|
||||
if (mActiveTransitions.get(i).mTransition != transition) continue;
|
||||
mixed = mActiveTransitions.remove(i);
|
||||
break;
|
||||
}
|
||||
if (mixed == null) return false;
|
||||
|
||||
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
|
||||
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
|
||||
finishCallback);
|
||||
} else {
|
||||
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
|
||||
+ mixed.mType);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
|
||||
@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 while Split-Screen is active.");
|
||||
TransitionInfo.Change pipChange = null;
|
||||
TransitionInfo.Change wallpaper = null;
|
||||
final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK);
|
||||
boolean homeIsOpening = false;
|
||||
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);
|
||||
} 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;
|
||||
mixed.mFinishCallback = finishCallback;
|
||||
Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
|
||||
--mixed.mInFlightSubAnimations;
|
||||
if (mixed.mInFlightSubAnimations > 0) return;
|
||||
if (isGoingHome) {
|
||||
mSplitHandler.onTransitionAnimationComplete();
|
||||
}
|
||||
mixed.mFinishCallback.onTransitionFinished(wct, wctCB);
|
||||
};
|
||||
if (isGoingHome) {
|
||||
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();
|
||||
// Let split update internal state for dismiss.
|
||||
mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
|
||||
finishCB);
|
||||
// Dispatch the rest of the transition normally. This will most-likely be taken by
|
||||
// recents or default handler.
|
||||
mPlayer.dispatchTransition(mixed.mTransition, everythingElse, otherStartT,
|
||||
finishTransaction, finishCB, this);
|
||||
} 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;
|
||||
mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction,
|
||||
finishCB);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionMerged(@NonNull IBinder transition) {
|
||||
MixedTransition mixed = null;
|
||||
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
|
||||
if (mActiveTransitions.get(i).mTransition != transition) continue;
|
||||
mixed = mActiveTransitions.remove(i);
|
||||
break;
|
||||
}
|
||||
if (mixed == null) return;
|
||||
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
|
||||
mPipHandler.onTransitionMerged(transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,33 +435,42 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
|
||||
}
|
||||
|
||||
boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
|
||||
return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
|
||||
(wct, cb) -> onFinish(active.mToken, wct, cb));
|
||||
}
|
||||
|
||||
void playTransition(@NonNull ActiveTransition active) {
|
||||
private void playTransition(@NonNull ActiveTransition active) {
|
||||
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
|
||||
|
||||
// If a handler already chose to run this animation, try delegating to it first.
|
||||
if (active.mHandler != null) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
|
||||
active.mHandler);
|
||||
if (startAnimation(active, active.mHandler)) {
|
||||
boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo,
|
||||
active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb));
|
||||
if (consumed) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Otherwise give every other handler a chance (in order)
|
||||
// Otherwise give every other handler a chance
|
||||
active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
|
||||
active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb), active.mHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives every handler (in order) a chance to animate until one consumes the transition.
|
||||
* @return the handler which consumed the transition.
|
||||
*/
|
||||
TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
|
||||
@NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) {
|
||||
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
||||
if (mHandlers.get(i) == active.mHandler) continue;
|
||||
if (mHandlers.get(i) == skip) continue;
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
|
||||
mHandlers.get(i));
|
||||
if (startAnimation(active, mHandlers.get(i))) {
|
||||
boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
|
||||
finishCB);
|
||||
if (consumed) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
|
||||
mHandlers.get(i));
|
||||
active.mHandler = mHandlers.get(i);
|
||||
return;
|
||||
return mHandlers.get(i);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
|
||||
@@ -333,7 +333,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
||||
info.addChange(mainChange);
|
||||
info.addChange(sideChange);
|
||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
|
||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(
|
||||
new WindowContainerTransaction(), mStageCoordinator,
|
||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
|
||||
boolean accepted = mStageCoordinator.startAnimation(transition, info,
|
||||
@@ -355,7 +355,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
||||
info.addChange(mainChange);
|
||||
info.addChange(sideChange);
|
||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
|
||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(
|
||||
new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
|
||||
STAGE_TYPE_SIDE);
|
||||
mMainStage.onTaskVanished(mMainChild);
|
||||
|
||||
Reference in New Issue
Block a user