Merge "Integrate pip to split flow with shell-transition"

This commit is contained in:
TreeHugger Robot
2021-12-30 00:39:57 +00:00
committed by Android (Google) Code Review
9 changed files with 185 additions and 70 deletions

View File

@@ -309,10 +309,11 @@ public class WMShellModule {
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
PhonePipMenuController pipMenuController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
pipSurfaceTransactionHelper);
pipSurfaceTransactionHelper, splitScreenOptional);
}
@WMSingleton

View File

@@ -25,6 +25,8 @@ import static android.util.RotationUtils.rotateBounds;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START;
@@ -40,6 +42,10 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -394,6 +400,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (ENABLE_SHELL_TRANSITIONS) {
if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo,
isPipTopLeft()
? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
mPipTransitionController.startExitTransition(
TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */);
return;
}
}
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
@@ -414,7 +432,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mPipTransitionController.startTransition(destinationBounds, wct);
mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
return;
}
mSyncTransactionQueue.queue(wct);
@@ -479,7 +497,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setBounds(mToken, null);
wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.reorder(mToken, false);
mPipTransitionController.startTransition(null, wct);
mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
null /* destinationBounds */);
return;
}
@@ -1280,7 +1299,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
@PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
mSplitScreenOptional.ifPresent(splitScreenController ->
splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
} else {
mTaskOrganizer.applyTransaction(wct);
}

View File

@@ -30,8 +30,9 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -51,8 +52,11 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
/**
* Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and
* exit animation.
@@ -64,6 +68,7 @@ public class PipTransition extends PipTransitionController {
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private Rect mExitDestinationBounds = new Rect();
@@ -77,13 +82,15 @@ public class PipTransition extends PipTransitionController {
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mSplitScreenOptional = splitScreenOptional;
}
@Override
@@ -101,13 +108,12 @@ public class PipTransition extends PipTransitionController {
}
@Override
public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
if (destinationBounds != null) {
mExitDestinationBounds.set(destinationBounds);
mExitTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
} else {
mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
}
mExitTransition = mTransitions.startTransition(type, out, this);
}
@Override
@@ -116,9 +122,15 @@ public class PipTransition extends PipTransitionController {
@android.annotation.NonNull SurfaceControl.Transaction startTransaction,
@android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) {
final int type = info.getType();
if (mExitTransition == transition) {
mExitTransition = null;
if (type == TRANSIT_EXIT_PIP_TO_SPLIT) {
return startExitToSplitAnimation(
info, startTransaction, finishTransaction, finishCallback);
}
if (info.getChanges().size() == 1) {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null, null);
@@ -138,7 +150,7 @@ public class PipTransition extends PipTransitionController {
}
}
if (info.getType() == TRANSIT_REMOVE_PIP) {
if (type == TRANSIT_REMOVE_PIP) {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
@@ -154,7 +166,7 @@ public class PipTransition extends PipTransitionController {
// We only 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) {
if (type != TRANSIT_PIP && type != TRANSIT_OPEN) {
// In case the PIP window is part of rotation transition, reset the bounds and rounded
// corner.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -368,6 +380,40 @@ public class PipTransition extends PipTransitionController {
return true;
}
private boolean startExitToSplitAnimation(TransitionInfo info,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
Transitions.TransitionFinishCallback finishCallback) {
final int changeSize = info.getChanges().size();
if (changeSize < 4) {
throw new RuntimeException(
"Got an exit-pip-to-split transition with unexpected change-list");
}
for (int i = changeSize - 1; i >= 0; i--) {
final TransitionInfo.Change change = info.getChanges().get(i);
final int mode = change.getMode();
if (mode == TRANSIT_CHANGE && change.getParent() != null) {
// TODO: perform resize/expand animation for reparented child task.
continue;
}
if (isOpeningType(mode) && change.getParent() == null) {
final SurfaceControl leash = change.getLeash();
final Rect endBounds = change.getEndAbsBounds();
startTransaction
.show(leash)
.setAlpha(leash, 1f)
.setPosition(leash, endBounds.left, endBounds.top)
.setWindowCrop(leash, endBounds.width(), endBounds.height());
}
}
mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
startTransaction.apply();
finishCallback.onTransitionFinished(null, null);
return true;
}
private void finishResizeForMenu(Rect destinationBounds) {
mPipMenuController.movePipMenu(null, null, destinationBounds);
mPipMenuController.updateMenuBounds(destinationBounds);

View File

@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.ComponentName;
@@ -98,9 +99,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/**
* Called when the Shell wants to starts a transition/animation.
* Called when the Shell wants to start an exit Pip transition/animation.
*/
public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
// Default implementation does nothing.
}

View File

@@ -239,6 +239,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
}
public void prepareEnterSplitScreen(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition);
}
public void finishEnterSplitScreen(SurfaceControl.Transaction t) {
mStageCoordinator.finishEnterSplitScreen(t);
}
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
final int stagePosition =

View File

@@ -28,7 +28,8 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
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;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -148,7 +149,7 @@ class SplitScreenTransitions {
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
}
boolean isOpening = isOpeningType(info.getType());
boolean isOpening = isOpeningTransition(info);
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
// fade in
startExampleAnimation(leash, true /* show */);
@@ -305,6 +306,12 @@ class SplitScreenTransitions {
mTransitions.getAnimExecutor().execute(va::start);
}
private boolean isOpeningTransition(TransitionInfo info) {
return Transitions.isOpeningType(info.getType())
|| info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE
|| info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
}
/** Bundled information of dismiss transition. */
static class DismissTransition {
IBinder mTransition;

View File

@@ -46,6 +46,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
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;
import static com.android.wm.shell.transition.Transitions.isClosingType;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -323,7 +324,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
}
mTaskOrganizer.applyTransaction(wct);
if (ENABLE_SHELL_TRANSITIONS) {
prepareEnterSplitScreen(wct);
mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
wct, null, this);
} else {
mTaskOrganizer.applyTransaction(wct);
}
return true;
}
@@ -720,12 +728,47 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
*/
void prepareExitSplitScreen(@StageType int stageToTop,
private void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED);
}
/**
* Prepare transaction to active split screen. If there's a task indicated, the task will be put
* into side stage.
*/
void prepareEnterSplitScreen(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
if (mMainStage.isActive()) return;
if (taskInfo != null) {
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
}
mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */);
mSideStage.moveToTop(getSideStageBounds(), wct);
}
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
mSplitLayout.init();
setDividerVisibility(true, t);
setSplitsVisible(true);
mShouldUpdateRecents = true;
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
}
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
outTopOrLeftBounds.set(mSplitLayout.getBounds1());
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
@@ -932,8 +975,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
mSideStage.moveToTop(getSideStageBounds(), wct);
prepareEnterSplitScreen(wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
updateSurfaceBounds(mSplitLayout, t);
@@ -1237,8 +1279,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
mSideStage.moveToTop(getSideStageBounds(), out);
prepareEnterSplitScreen(out);
mSplitTransitions.mPendingEnter = transition;
}
}
@@ -1250,18 +1291,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Once the pending enter transition got merged, make sure to bring divider bar visible and
// clear the pending transition from cache to prevent mess-up the following state.
if (transition == mSplitTransitions.mPendingEnter) {
mSplitLayout.init();
setDividerVisibility(true, null /* transaction */);
setSplitsVisible(true);
mShouldUpdateRecents = true;
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
finishEnterSplitScreen(null);
mSplitTransitions.mPendingEnter = null;
}
}
@@ -1349,47 +1379,40 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
sideChild = change;
}
}
if (mainChild == null || sideChild == null) {
throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+ " 2 tasks in transition. Possibly one of them failed to launch");
// TODO: fallback logic. Probably start a new transition to exit split before
// applying anything here. Ideally consolidate with transition-merging.
// TODO: fallback logic. Probably start a new transition to exit split before applying
// anything here. Ideally consolidate with transition-merging.
if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
if (mainChild == null && sideChild == null) {
throw new IllegalStateException("Launched a task in split, but didn't receive any"
+ " task in transition.");
}
} else {
if (mainChild == null || sideChild == null) {
throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+ " 2 tasks in transition. Possibly one of them failed to launch");
}
}
// Update local states (before animating).
mSplitLayout.init();
setDividerVisibility(true, t);
setSplitsVisible(true);
addDividerBarToTransition(info, t, true /* show */);
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
// aren't serialized with transition callbacks.
// TODO(b/184679596): Find a way to either include task-org information in
// the transition, or synchronize task-org callbacks.
if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ " to have been called with " + mainChild.getTaskInfo().taskId
+ " before startAnimation().");
}
if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
+ " to have been called with " + sideChild.getTaskInfo().taskId
+ " before startAnimation().");
}
mShouldUpdateRecents = true;
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
finishEnterSplitScreen(t);
addDividerBarToTransition(info, t, true /* show */);
return true;
}

View File

@@ -302,6 +302,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
// Clear overridden bounds and windowing mode to make sure the child task can inherit
// windowing mode and bounds from split root.
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
.setBounds(task.token, null);
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
}

View File

@@ -74,23 +74,25 @@ public class Transitions implements RemoteCallable<Transitions> {
public static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
/** Transition type for dismissing split-screen via dragging the divider off the screen. */
public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 1;
/** Transition type for launching 2 tasks simultaneously. */
public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
/** Transition type for exiting PIP via the Shell, via pressing the expand button. */
public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
public static final int TRANSIT_EXIT_PIP_TO_SPLIT = TRANSIT_FIRST_CUSTOM + 2;
/** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3;
/** Transition type for launching 2 tasks simultaneously. */
public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
/** Transition type for dismissing split-screen via dragging the divider off the screen. */
public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6;
/** Transition type for dismissing split-screen. */
public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7;
private final WindowOrganizer mOrganizer;
private final Context mContext;