Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57b8e89e authored by Tony Huang's avatar Tony Huang
Browse files

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
parent 6c78bab2
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -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,8 +1524,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mSurfaceTransactionHelper.round(tx, mLeash, isInPip());

        wct.setBounds(mToken, taskBounds);
        // 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);
        }
    }

    /**
     * Applies the window container transaction to finish a bounds resize.
+5 −9
Original line number Diff line number Diff line
@@ -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) {
+28 −41
Original line number Diff line number Diff line
@@ -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) {
            targetStage = mSideStage;
            sideStagePosition = stagePosition;
        } else {
        if (isSplitScreenVisible()) {
                // If the split screen is activated, retrieves target stage based on position.
            // If the split screen is foreground, 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);
        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 */);
            targetStage.evictAllChildren(wct);
            if (!isSplitScreenVisible()) {
                final StageTaskListener anotherStage = targetStage == mMainStage
                        ? mSideStage : mMainStage;
                anotherStage.reparentTopTask(wct);
                anotherStage.evictAllChildren(wct);
                wct.reorder(mRootTaskInfo.token, true);
            }
        mTaskOrganizer.applyTransaction(wct);
            setRootForceTranslucent(false, wct);
            mSyncQueue.queue(wct);
        }

        // Due to drag already pip task entering split by this method so need to reset flag here.
        mIsDropEntering = false;
        return true;
    }

+32 −23
Original line number Diff line number Diff line
@@ -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, 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 testMoveToStage_splitActiveForeground() {
        when(mStageCoordinator.isSplitActive()).thenReturn(true);
        when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
        // 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, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
                new WindowContainerTransaction());
        verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
        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, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
                new WindowContainerTransaction());
        verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, 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 testMoveToUndefinedStage() {
    public void testMoveToStage_splitInctive() {
        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
        final WindowContainerTransaction wct = new WindowContainerTransaction();

        // 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));
        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());

        // Verify move to undefined stage after split screen activated moves task based on position.
        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());
    }

    @Test