Merge "Add fallback logic to cancel entering split screen" into tm-qpr-dev am: b8a27eb601

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20185503

Change-Id: I1565bc4b8fb6d0800c49357137525608c6e8cdc9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jerry Chang
2022-10-27 04:34:28 +00:00
committed by Automerger Merge Worker
3 changed files with 175 additions and 128 deletions

View File

@@ -108,6 +108,14 @@ class SplitScreenTransitions {
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
final TransitSession pendingTransition = getPendingTransition(transition);
if (pendingTransition != null && pendingTransition.mCanceled) {
// The pending transition was canceled, so skip playing animation.
t.apply();
onFinish(null /* wct */, null /* wctCB */);
return;
}
// Play some place-holder fade animations
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -170,9 +178,7 @@ class SplitScreenTransitions {
}
boolean isPendingTransition(IBinder transition) {
return isPendingEnter(transition)
|| isPendingDismiss(transition)
|| isPendingRecent(transition);
return getPendingTransition(transition) != null;
}
boolean isPendingEnter(IBinder transition) {
@@ -187,22 +193,38 @@ class SplitScreenTransitions {
return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
}
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
return mPendingEnter;
} else if (isPendingRecent(transition)) {
return mPendingRecent;
} else if (isPendingDismiss(transition)) {
return mPendingDismiss;
}
return null;
}
/** Starts a transition to enter split with a remote transition animator. */
IBinder startEnterTransition(
@WindowManager.TransitionType int transitType,
WindowContainerTransaction wct,
@Nullable RemoteTransition remoteTransition,
Transitions.TransitionHandler handler,
@Nullable TransitionCallback callback) {
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishedCallback) {
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
setEnterTransition(transition, remoteTransition, callback);
setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback);
return transition;
}
/** Sets a transition to enter split. */
void setEnterTransition(@NonNull IBinder transition,
@Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
mPendingEnter = new TransitSession(transition, callback);
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishedCallback) {
mPendingEnter = new TransitSession(transition, consumedCallback, finishedCallback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -237,8 +259,9 @@ class SplitScreenTransitions {
}
void setRecentTransition(@NonNull IBinder transition,
@Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
mPendingRecent = new TransitSession(transition, callback);
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionFinishedCallback finishCallback) {
mPendingRecent = new TransitSession(transition, null /* consumedCb */, finishCallback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -248,7 +271,7 @@ class SplitScreenTransitions {
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter recent panel");
+ " deduced Enter recent panel");
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
@@ -256,14 +279,9 @@ class SplitScreenTransitions {
if (mergeTarget != mAnimatingTransition) return;
if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
mPendingRecent.mCallback = new TransitionCallback() {
@Override
public void onTransitionFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
// Since there's an entering transition merged, recent transition no longer
// need to handle entering split screen after the transition finished.
}
};
// Since there's an entering transition merged, recent transition no longer
// need to handle entering split screen after the transition finished.
mPendingRecent.setFinishedCallback(null);
}
if (mActiveRemoteHandler != null) {
@@ -277,7 +295,7 @@ class SplitScreenTransitions {
}
boolean end() {
// If its remote, there's nothing we can do right now.
// If It's remote, there's nothing we can do right now.
if (mActiveRemoteHandler != null) return false;
for (int i = mAnimations.size() - 1; i >= 0; --i) {
final Animator anim = mAnimations.get(i);
@@ -290,20 +308,20 @@ class SplitScreenTransitions {
@Nullable SurfaceControl.Transaction finishT) {
if (isPendingEnter(transition)) {
if (!aborted) {
// An enter transition got merged, appends the rest operations to finish entering
// An entering transition got merged, appends the rest operations to finish entering
// split screen.
mStageCoordinator.finishEnterSplitScreen(finishT);
mPendingRemoteHandler = null;
}
mPendingEnter.mCallback.onTransitionConsumed(aborted);
mPendingEnter.onConsumed(aborted);
mPendingEnter = null;
mPendingRemoteHandler = null;
} else if (isPendingDismiss(transition)) {
mPendingDismiss.mCallback.onTransitionConsumed(aborted);
mPendingDismiss.onConsumed(aborted);
mPendingDismiss = null;
} else if (isPendingRecent(transition)) {
mPendingRecent.mCallback.onTransitionConsumed(aborted);
mPendingRecent.onConsumed(aborted);
mPendingRecent = null;
mPendingRemoteHandler = null;
}
@@ -312,23 +330,16 @@ class SplitScreenTransitions {
void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
if (!mAnimations.isEmpty()) return;
TransitionCallback callback = null;
if (wct == null) wct = new WindowContainerTransaction();
if (isPendingEnter(mAnimatingTransition)) {
callback = mPendingEnter.mCallback;
mPendingEnter.onFinished(wct, mFinishTransaction);
mPendingEnter = null;
}
if (isPendingDismiss(mAnimatingTransition)) {
callback = mPendingDismiss.mCallback;
mPendingDismiss = null;
}
if (isPendingRecent(mAnimatingTransition)) {
callback = mPendingRecent.mCallback;
} else if (isPendingRecent(mAnimatingTransition)) {
mPendingRecent.onFinished(wct, mFinishTransaction);
mPendingRecent = null;
}
if (callback != null) {
if (wct == null) wct = new WindowContainerTransaction();
callback.onTransitionFinished(wct, mFinishTransaction);
} else if (isPendingDismiss(mAnimatingTransition)) {
mPendingDismiss.onFinished(wct, mFinishTransaction);
mPendingDismiss = null;
}
mPendingRemoteHandler = null;
@@ -363,10 +374,7 @@ class SplitScreenTransitions {
onFinish(null /* wct */, null /* wctCB */);
});
};
va.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { }
va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finisher.run();
@@ -376,9 +384,6 @@ class SplitScreenTransitions {
public void onAnimationCancel(Animator animation) {
finisher.run();
}
@Override
public void onAnimationRepeat(Animator animation) { }
});
mAnimations.add(va);
mTransitions.getAnimExecutor().execute(va::start);
@@ -432,24 +437,66 @@ class SplitScreenTransitions {
|| info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
}
/** Clean-up callbacks for transition. */
interface TransitionCallback {
/** Calls when the transition got consumed. */
default void onTransitionConsumed(boolean aborted) {}
/** Calls when the transition got consumed. */
interface TransitionConsumedCallback {
void onConsumed(boolean aborted);
}
/** Calls when the transition finished. */
default void onTransitionFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {}
/** Calls when the transition finished. */
interface TransitionFinishedCallback {
void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t);
}
/** Session for a transition and its clean-up callback. */
static class TransitSession {
final IBinder mTransition;
TransitionCallback mCallback;
TransitionConsumedCallback mConsumedCallback;
TransitionFinishedCallback mFinishedCallback;
TransitSession(IBinder transition, @Nullable TransitionCallback callback) {
/** Whether the transition was canceled. */
boolean mCanceled;
TransitSession(IBinder transition,
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishedCallback) {
mTransition = transition;
mCallback = callback != null ? callback : new TransitionCallback() {};
mConsumedCallback = consumedCallback;
mFinishedCallback = finishedCallback;
}
/** Sets transition consumed callback. */
void setConsumedCallback(@Nullable TransitionConsumedCallback callback) {
mConsumedCallback = callback;
}
/** Sets transition finished callback. */
void setFinishedCallback(@Nullable TransitionFinishedCallback callback) {
mFinishedCallback = callback;
}
/**
* Cancels the transition. This should be called before playing animation. A canceled
* transition will skip playing animation.
*
* @param finishedCb new finish callback to override.
*/
void cancel(@Nullable TransitionFinishedCallback finishedCb) {
mCanceled = true;
setFinishedCallback(finishedCb);
}
void onConsumed(boolean aborted) {
if (mConsumedCallback != null) {
mConsumedCallback.onConsumed(aborted);
}
}
void onFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
if (mFinishedCallback != null) {
mFinishedCallback.onFinished(finishWct, finishT);
}
}
}
@@ -459,7 +506,7 @@ class SplitScreenTransitions {
final @SplitScreen.StageType int mDismissTop;
DismissTransition(IBinder transition, int reason, int dismissTop) {
super(transition, null /* callback */);
super(transition, null /* consumedCallback */, null /* finishedCallback */);
this.mReason = reason;
this.mDismissTop = dismissTop;
}

View File

@@ -226,33 +226,36 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
};
private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback =
new SplitScreenTransitions.TransitionCallback() {
@Override
public void onTransitionFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
// Check if the recent transition is finished by returning to the current split, so we
// can restore the divider bar.
for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
final WindowContainerTransaction.HierarchyOp op =
finishWct.getHierarchyOps().get(i);
final IBinder container = op.getContainer();
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& (mMainStage.containsContainer(container)
|| mSideStage.containsContainer(container))) {
updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */);
setDividerVisibility(true, finishT);
return;
}
}
private final SplitScreenTransitions.TransitionFinishedCallback
mRecentTransitionFinishedCallback =
new SplitScreenTransitions.TransitionFinishedCallback() {
@Override
public void onFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
// Check if the recent transition is finished by returning to the current
// split, so we
// can restore the divider bar.
for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
final WindowContainerTransaction.HierarchyOp op =
finishWct.getHierarchyOps().get(i);
final IBinder container = op.getContainer();
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& (mMainStage.containsContainer(container)
|| mSideStage.containsContainer(container))) {
updateSurfaceBounds(mSplitLayout, finishT,
false /* applyResizingOffset */);
setDividerVisibility(true, finishT);
return;
}
}
// Dismiss the split screen if it's not returning to split.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
setSplitsVisible(false);
setDividerVisibility(false, finishT);
logExit(EXIT_REASON_UNKNOWN);
}
};
// Dismiss the split screen if it's not returning to split.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
setSplitsVisible(false);
setDividerVisibility(false, finishT);
logExit(EXIT_REASON_UNKNOWN);
}
};
protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
@@ -389,15 +392,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (ENABLE_SHELL_TRANSITIONS) {
prepareEnterSplitScreen(wct);
mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
null, this, new SplitScreenTransitions.TransitionCallback() {
@Override
public void onTransitionFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
if (!evictWct.isEmpty()) {
finishWct.merge(evictWct, true);
}
null, this, null /* consumedCallback */, (finishWct, finishT) -> {
if (!evictWct.isEmpty()) {
finishWct.merge(evictWct, true);
}
});
} /* finishedCallback */);
} else {
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
@@ -434,28 +433,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
wct.sendPendingIntent(intent, fillInIntent, options);
// If split screen is not activated, we're expecting to open a pair of apps to split.
final int transitType = mMainStage.isActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
prepareEnterSplitScreen(wct, null /* taskInfo */, position);
mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this,
new SplitScreenTransitions.TransitionCallback() {
@Override
public void onTransitionConsumed(boolean aborted) {
// Switch the split position if launching as MULTIPLE_TASK failed.
if (aborted
&& (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
setSideStagePositionAnimated(
SplitLayout.reversePosition(mSideStagePosition));
}
mSplitTransitions.startEnterTransition(transitType, wct, null, this,
aborted -> {
// Switch the split position if launching as MULTIPLE_TASK failed.
if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
setSideStagePositionAnimated(
SplitLayout.reversePosition(mSideStagePosition));
}
@Override
public void onTransitionFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
if (!evictWct.isEmpty()) {
finishWct.merge(evictWct, true);
}
} /* consumedCallback */,
(finishWct, finishT) -> {
if (!evictWct.isEmpty()) {
finishWct.merge(evictWct, true);
}
});
} /* finishedCallback */);
}
/** Launches an activity into split by legacy transition. */
@@ -564,9 +560,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/**
* Starts with the second task to a split pair in one transition.
*
* @param wct transaction to start the first task
* @param wct transaction to start the first task
* @param instanceId if {@code null}, will not log. Otherwise it will be used in
* {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
* {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
*/
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, float splitRatio,
@@ -592,7 +588,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.startTask(mainTaskId, mainOptions);
mSplitTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null);
setEnterInstanceId(instanceId);
}
@@ -639,7 +635,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
/**
* @param wct transaction to start the first task
* @param wct transaction to start the first task
* @param instanceId if {@code null}, will not log. Otherwise it will be used in
* {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
*/
@@ -1082,15 +1078,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
switch (exitReason) {
// One of the apps doesn't support MW
case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
// User has explicitly dragged the divider to dismiss split
// User has explicitly dragged the divider to dismiss split
case EXIT_REASON_DRAG_DIVIDER:
// Either of the split apps have finished
// Either of the split apps have finished
case EXIT_REASON_APP_FINISHED:
// One of the children enters PiP
// One of the children enters PiP
case EXIT_REASON_CHILD_TASK_ENTER_PIP:
// One of the apps occludes lock screen.
// One of the apps occludes lock screen.
case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
// User has unlocked the device after folded
// User has unlocked the device after folded
case EXIT_REASON_DEVICE_FOLDED:
return true;
default:
@@ -1839,7 +1835,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|| activityType == ACTIVITY_TYPE_RECENTS) {
// Enter overview panel, so start recent transition.
mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
mRecentTransitionCallback);
mRecentTransitionFinishedCallback);
} else if (mSplitTransitions.mPendingRecent == null) {
// If split-task is not controlled by recents animation
// and occluded by the other fullscreen task, dismiss both.
@@ -1853,8 +1849,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(
transition, request.getRemoteTransition(), null /* callback */);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
null /* consumedCallback */, null /* finishedCallback */);
}
}
return out;
@@ -1873,7 +1869,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
final @WindowManager.TransitionType int type = request.getType();
if (isSplitActive() && !isOpeningType(type)
&& (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
&& (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. "
@@ -2031,17 +2027,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
// TODO(b/250853925): 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.");
Log.w(TAG, "Launched a task in split, but didn't receive any task in transition.");
mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */);
return true;
}
} else {
if (mainChild == null || sideChild == null) {
throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
Log.w(TAG, "Launched 2 tasks in split, but didn't receive"
+ " 2 tasks in transition. Possibly one of them failed to launch");
final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN :
(sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED);
mSplitTransitions.mPendingEnter.cancel(
(cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct));
return true;
}
}
@@ -2305,7 +2305,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stageType, wct);
mSplitTransitions.startDismissTransition(wct,StageCoordinator.this, stageType,
mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
mSplitUnsupportedToast.show();
}

View File

@@ -181,7 +181,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
new RemoteTransition(testRemote), mStageCoordinator, null);
new RemoteTransition(testRemote), mStageCoordinator, null, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -421,7 +421,7 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null);
new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,