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

Commit d5405aba authored by Tony Huang's avatar Tony Huang
Browse files

Make pip task could be enter split

This CL migrate the pip to split flow. Before this CL, split side
always start a enter transition after drop app. But pip side also
start another transition when they got restart attemp and lead to
two transition here(split enter transition will got exception case
here due to the target pip task didn't include in transition change
which caused by it already on front). In this case, split should just
launch the intent and let pip side to handle itself transition.

This CL only handle start single intent case, need to take
another look to handle all cases such as shortcut, or open
two intents, tasks case.

Bug: 282134052
Bug: 282902244
Bug: 284089421
Test: manual
Test: pass existing tests
Change-Id: Iaf4f5e305ea8f46be786f0316b4f8a3d2a14c3e9
parent 96a07b11
Loading
Loading
Loading
Loading
+21 −25
Original line number Diff line number Diff line
@@ -528,17 +528,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            return;
        }

        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 displayBounds = mPipBoundsState.getDisplayBounds();
        final Rect destinationBounds = new Rect(displayBounds);
        final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
@@ -547,10 +536,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        // For exiting to fullscreen, the windowing mode of task will be changed to fullscreen
        // until the animation is finished. Otherwise if the activity is resumed and focused at the
        // begin of aniamtion, the app may do something too early to distub the animation.
        final boolean toFullscreen = destinationBounds.equals(displayBounds);

        if (Transitions.SHELL_TRANSITIONS_ROTATION || (Transitions.ENABLE_SHELL_TRANSITIONS
                && !toFullscreen)) {
        if (Transitions.SHELL_TRANSITIONS_ROTATION) {
            // When exit to fullscreen with Shell transition enabled, we update the Task windowing
            // mode directly so that it can also trigger display rotation and visibility update in
            // the same transition if there will be any.
@@ -580,9 +567,29 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);

        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
                wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
                mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo,
                        isPipToTopLeft()
                                ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
                mPipTransitionController.startExitTransition(
                        TRANSIT_EXIT_PIP_TO_SPLIT, wct, destinationBounds);
                return;
            }

            if (mSplitScreenOptional.isPresent()) {
                // If pip activity will reparent to origin task case and if the origin task still
                // under split root, apply exit split transaction to make it expand to fullscreen.
                SplitScreenController split = mSplitScreenOptional.get();
                if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) {
                    split.prepareExitSplitScreen(wct, split.getStageOfTask(
                            mTaskInfo.lastParentTaskIdBeforePip));
                }
            }
            mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
            return;
        }

        if (mSplitScreenOptional.isPresent()) {
            // If pip activity will reparent to origin task case and if the origin task still under
            // split root, just exit split screen here to ensure it could expand to fullscreen.
@@ -1634,17 +1641,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        }
    }

    private boolean isPipTopLeft() {
        if (!mSplitScreenOptional.isPresent()) {
            return false;
        }
        final Rect topLeft = new Rect();
        final Rect bottomRight = new Rect();
        mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);

        return topLeft.contains(mPipBoundsState.getBounds());
    }

    private boolean isPipToTopLeft() {
        if (!mSplitScreenOptional.isPresent()) {
            return false;
+1 −6
Original line number Diff line number Diff line
@@ -980,12 +980,7 @@ public class PipTransition extends PipTransitionController {
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback,
            @NonNull TaskInfo taskInfo) {
        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--) {
        for (int i = info.getChanges().size() - 1; i >= 0; i--) {
            final TransitionInfo.Change change = info.getChanges().get(i);
            final int mode = change.getMode();

+8 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;

import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;

@@ -223,6 +224,13 @@ public abstract class PipTransitionController implements Transitions.TransitionH
        return false;
    }

    /** Whether a particular package is same as current pip package. */
    public boolean isInPipPackage(String packageName) {
        final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
        return packageName != null && inPipTask != null
                && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
    }

    /** Add PiP-related changes to `outWCT` for the given request. */
    public void augmentRequest(@NonNull IBinder transition,
            @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
+31 −7
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ 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;
@@ -332,6 +333,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
        return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
    }

    /** Get the split stage of task is under it. */
    public @StageType int getStageOfTask(int taskId) {
        return mStageCoordinator.getStageOfTask(taskId);
    }

    /** Check split is foreground and task is under split or not by taskId. */
    public boolean isTaskInSplitScreenForeground(int taskId) {
        return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
@@ -378,17 +384,35 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
        mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
    }

    public void enterSplitScreen(int taskId, boolean leftOrTop) {
        enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
    }

    /**
     * Doing necessary window transaction for other transition handler need to enter split in
     * transition.
     */
    public void prepareEnterSplitScreen(WindowContainerTransaction wct,
            ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
        mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition);
        mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition,
                false /* resizeAnim */);
    }

    /**
     * Doing necessary surface transaction for other transition handler need to enter split in
     * transition when finished.
     */
    public void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
        mStageCoordinator.finishEnterSplitScreen(finishT);
    }

    public void finishEnterSplitScreen(SurfaceControl.Transaction t) {
        mStageCoordinator.finishEnterSplitScreen(t);
    /**
     * Doing necessary window transaction for other transition handler need to exit split in
     * transition.
     */
    public void prepareExitSplitScreen(WindowContainerTransaction wct,
            @StageType int stageToTop) {
        mStageCoordinator.prepareExitSplitScreen(stageToTop, wct);
    }

    public void enterSplitScreen(int taskId, boolean leftOrTop) {
        enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
    }

    public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
+34 −24
Original line number Diff line number Diff line
@@ -393,7 +393,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,

    boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
            WindowContainerTransaction wct) {
        prepareEnterSplitScreen(wct, task, stagePosition);
        prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */);
        if (ENABLE_SHELL_TRANSITIONS) {
            mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
                    null, this, null /* consumedCallback */, null /* finishedCallback */,
@@ -491,20 +491,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    /** Launches an activity into split. */
    void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
            @Nullable Bundle options) {
        mSplitRequest = new SplitRequest(intent.getIntent(), position);
        if (!ENABLE_SHELL_TRANSITIONS) {
            startIntentLegacy(intent, fillInIntent, position, options);
            return;
        }

        final WindowContainerTransaction wct = new WindowContainerTransaction();

        options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
        wct.sendPendingIntent(intent, fillInIntent, options);

        // If this should be mixed, just send the intent to avoid split handle transition directly.
        if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(intent)) {
            mTaskOrganizer.applyTransaction(wct);
            return;
        }

        // If split screen is not activated, we're expecting to open a pair of apps to split.
        final int extraTransitType = mMainStage.isActive()
                ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
        prepareEnterSplitScreen(wct, null /* taskInfo */, position);
        prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);

        mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
                null /* consumedCallback */, null /* finishedCallback */, extraTransitType,
@@ -562,7 +568,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        if (isEnteringSplit && mLogger.isEnterRequestedByDrag()) {
            updateWindowBounds(mSplitLayout, wct);
        }
        mSplitRequest = new SplitRequest(intent.getIntent(), position);
        wct.sendPendingIntent(intent, fillInIntent, options);
        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
    }
@@ -1448,7 +1453,7 @@ public 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.
     */
    private void prepareExitSplitScreen(@StageType int stageToTop,
    void prepareExitSplitScreen(@StageType int stageToTop,
            @NonNull WindowContainerTransaction wct) {
        if (!mMainStage.isActive()) return;
        mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
@@ -1458,7 +1463,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    }

    private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
        prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED);
        prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED,
                !mIsDropEntering);
    }

    /**
@@ -1466,17 +1472,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
     * into side stage.
     */
    void prepareEnterSplitScreen(WindowContainerTransaction wct,
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
            boolean resizeAnim) {
        onSplitScreenEnter();
        if (isSplitActive()) {
            prepareBringSplit(wct, taskInfo, startPosition);
            prepareBringSplit(wct, taskInfo, startPosition, resizeAnim);
        } else {
            prepareActiveSplit(wct, taskInfo, startPosition);
            prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim);
        }
    }

    private void prepareBringSplit(WindowContainerTransaction wct,
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
            boolean resizeAnim) {
        if (taskInfo != null) {
            wct.startTask(taskInfo.taskId,
                    resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
@@ -1484,12 +1492,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        // If running background, we need to reparent current top visible task to main stage.
        if (!isSplitScreenVisible()) {
            mMainStage.reparentTopTask(wct);
            prepareSplitLayout(wct);
            prepareSplitLayout(wct, resizeAnim);
        }
    }

    private void prepareActiveSplit(WindowContainerTransaction wct,
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
            boolean resizeAnim) {
        if (!ENABLE_SHELL_TRANSITIONS) {
            // Legacy transition we need to create divider here, shell transition case we will
            // create it on #finishEnterSplitScreen
@@ -1500,17 +1509,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            mSideStage.addTask(taskInfo, wct);
        }
        mMainStage.activate(wct, true /* includingTopTask */);
        prepareSplitLayout(wct);
        prepareSplitLayout(wct, resizeAnim);
    }

    private void prepareSplitLayout(WindowContainerTransaction wct) {
        if (mIsDropEntering) {
            mSplitLayout.resetDividerPosition();
        } else {
    private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) {
        if (resizeAnim) {
            mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
        } else {
            mSplitLayout.resetDividerPosition();
        }
        updateWindowBounds(mSplitLayout, wct);
        if (!mIsDropEntering) {
        if (resizeAnim) {
            // Reset its smallest width dp to avoid is change layout before it actually resized to
            // split bounds.
            wct.setSmallestScreenWidthDp(mMainStage.mRootTaskInfo.token,
@@ -1520,21 +1529,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        setRootForceTranslucent(false, wct);
    }

    void finishEnterSplitScreen(SurfaceControl.Transaction t) {
        mSplitLayout.update(t);
    void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
        mSplitLayout.update(finishT);
        mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
                getMainStageBounds());
        mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash,
                getSideStageBounds());
        setDividerVisibility(true, t);
        setDividerVisibility(true, finishT);
        // Ensure divider surface are re-parented back into the hierarchy at the end of the
        // transition. See Transition#buildFinishTransaction for more detail.
        t.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
        finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);

        updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
        t.show(mRootTaskLeash);
        updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */);
        finishT.show(mRootTaskLeash);
        setSplitsVisible(true);
        mIsDropEntering = false;
        mSplitRequest = null;
        updateRecentTasksSplitPair();
        if (!mLogger.hasStartedSession()) {
            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
Loading