Improve pip to split transition

When pip to split, flicker usually occured due to the surface
reset transaction applied too late and cuase double offset lead to
surface outside of display then cause flicker.

Fix it by using sync transaction, and also try to do this transition
in a WCT rather than two.
(Before this CL, we will add pip task to split root as one WCT, and
active split as another WCT).

Fix: 272446831
Test: manual
Test: pass existing tests
Change-Id: I6d59bd4084862e15a337c755cbad73d26a71e5aa
This commit is contained in:
Tony Huang
2023-03-09 10:40:49 +00:00
parent 6c78bab2e3
commit 57b8e89e12
4 changed files with 79 additions and 80 deletions

View File

@@ -1481,9 +1481,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
applyFinishBoundsResize(wct, direction, false);
}
} else {
final boolean isPipTopLeft =
direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && isPipToTopLeft();
applyFinishBoundsResize(wct, direction, isPipTopLeft);
applyFinishBoundsResize(wct, direction, isPipToTopLeft());
// Use sync transaction to apply finish transaction for enter split case.
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
mSyncTransactionQueue.runInSync(t -> {
t.merge(tx);
});
}
}
finishResizeForMenu(destinationBounds);
@@ -1520,7 +1524,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper.round(tx, mLeash, isInPip());
wct.setBounds(mToken, taskBounds);
wct.setBoundsChangeTransaction(mToken, tx);
// Pip to split should use sync transaction to sync split bounds change.
if (direction != TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
wct.setBoundsChangeTransaction(mToken, tx);
}
}
/**

View File

@@ -31,7 +31,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -89,7 +88,6 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -339,8 +337,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
new WindowContainerTransaction());
return moveToStage(taskId, sideStagePosition, new WindowContainerTransaction());
}
/**
@@ -351,13 +348,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.updateSurfaces(transaction);
}
private boolean moveToStage(int taskId, @StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
private boolean moveToStage(int taskId, @SplitPosition int stagePosition,
WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
}
return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct);
return mStageCoordinator.moveToStage(task, stagePosition, wct);
}
public boolean removeFromSideStage(int taskId) {
@@ -382,10 +379,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
final int stagePosition =
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
moveToStage(taskId, stageType, stagePosition, wct);
moveToStage(taskId, stagePosition, wct);
}
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {

View File

@@ -399,56 +399,43 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return STAGE_TYPE_UNDEFINED;
}
boolean moveToStage(ActivityManager.RunningTaskInfo task, @StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
WindowContainerTransaction wct) {
StageTaskListener targetStage;
int sideStagePosition;
if (stageType == STAGE_TYPE_MAIN) {
targetStage = mMainStage;
sideStagePosition = reverseSplitPosition(stagePosition);
} else if (stageType == STAGE_TYPE_SIDE) {
if (isSplitScreenVisible()) {
// If the split screen is foreground, retrieves target stage based on position.
targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
sideStagePosition = mSideStagePosition;
} else {
targetStage = mSideStage;
sideStagePosition = stagePosition;
} else {
if (isSplitScreenVisible()) {
// If the split screen is activated, retrieves target stage based on position.
targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
sideStagePosition = mSideStagePosition;
} else {
// Exit split if it running background.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
targetStage = mSideStage;
sideStagePosition = stagePosition;
}
}
if (!isSplitActive()) {
// prevent the fling divider to center transitioni if split screen didn't active.
mIsDropEntering = true;
mSplitLayout.init();
prepareEnterSplitScreen(wct, task, stagePosition);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
});
} else {
setSideStagePosition(sideStagePosition, wct);
targetStage.addTask(task, wct);
targetStage.evictAllChildren(wct);
if (!isSplitScreenVisible()) {
final StageTaskListener anotherStage = targetStage == mMainStage
? mSideStage : mMainStage;
anotherStage.reparentTopTask(wct);
anotherStage.evictAllChildren(wct);
wct.reorder(mRootTaskInfo.token, true);
}
setRootForceTranslucent(false, wct);
mSyncQueue.queue(wct);
}
setSideStagePosition(sideStagePosition, wct);
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
targetStage.evictAllChildren(evictWct);
// Apply surface bounds before animation start.
SurfaceControl.Transaction startT = mTransactionPool.acquire();
if (startT != null) {
updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
startT.apply();
mTransactionPool.release(startT);
}
// reparent the task to an invisible split root will make the activity invisible. Reorder
// the root task to front to make the entering transition from pip to split smooth.
wct.reorder(mRootTaskInfo.token, true);
wct.reorder(targetStage.mRootTaskInfo.token, true);
targetStage.addTask(task, wct);
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
}
mTaskOrganizer.applyTransaction(wct);
// Due to drag already pip task entering split by this method so need to reset flag here.
mIsDropEntering = false;
return true;
}

View File

@@ -144,39 +144,48 @@ public class StageCoordinatorTests extends ShellTestCase {
}
@Test
public void testMoveToStage() {
public void testMoveToStage_splitActiveBackground() {
when(mStageCoordinator.isSplitActive()).thenReturn(true);
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.moveToStage(task, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
new WindowContainerTransaction());
verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
mStageCoordinator.moveToStage(task, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
new WindowContainerTransaction());
verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
verify(mSideStage).addTask(eq(task), eq(wct));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
}
@Test
public void testMoveToUndefinedStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
// Verify move to undefined stage while split screen not activated moves task to side stage.
when(mStageCoordinator.isSplitScreenVisible()).thenReturn(false);
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
new WindowContainerTransaction());
verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
// Verify move to undefined stage after split screen activated moves task based on position.
public void testMoveToStage_splitActiveForeground() {
when(mStageCoordinator.isSplitActive()).thenReturn(true);
when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
new WindowContainerTransaction());
verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
// Assume current side stage is top or left.
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
verify(mMainStage).addTask(eq(task), eq(wct));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
mStageCoordinator.moveToStage(task, SPLIT_POSITION_TOP_OR_LEFT, wct);
verify(mSideStage).addTask(eq(task), eq(wct));
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
}
@Test
public void testMoveToStage_splitInctive() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
}
@Test