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

Commit 8531eb85 authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Additional interfaces for main/side stage split-screen model

Add some more interfaces APIs for manipulating main/side stage
split-screen.
Also, renamed pinTask/unpinTask to moveToSideStage/removeFromSideStage
for consistency.

Test: Command line
Bug: 176061049
Change-Id: Ic5b909c42e027e6f4809931c6dc92af1bf79b050
parent 13189ac1
Loading
Loading
Loading
Loading
+47 −11
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.wm.shell;

import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;

import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
@@ -107,10 +109,14 @@ public final class ShellCommandHandlerImpl {
                return runPair(args, pw);
            case "unpair":
                return runUnpair(args, pw);
            case "pinTask":
                return runPinTask(args, pw);
            case "unpinTask":
                return runUnpinTask(args, pw);
            case "moveToSideStage":
                return runMoveToSideStage(args, pw);
            case "removeFromSideStage":
                return runRemoveFromSideStage(args, pw);
            case "setSideStagePosition":
                return runSetSideStagePosition(args, pw);
            case "setSideStageVisibility":
                return runSetSideStageVisibility(args, pw);
            case "help":
                return runHelp(pw);
            default:
@@ -141,25 +147,50 @@ public final class ShellCommandHandlerImpl {
        return true;
    }

    private boolean runPinTask(String[] args, PrintWriter pw) {
    private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
        if (args.length < 3) {
            // First arguments are "WMShell" and command name.
            pw.println("Error: task id should be provided as arguments");
            return false;
        }
        final int taskId = new Integer(args[2]);
        mSplitScreenOptional.ifPresent(split -> split.pinTask(taskId));
        final int sideStagePosition = args.length > 3
                ? new Integer(args[3]) : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
        mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
        return true;
    }

    private boolean runUnpinTask(String[] args, PrintWriter pw) {
    private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
        if (args.length < 3) {
            // First arguments are "WMShell" and command name.
            pw.println("Error: task id should be provided as arguments");
            return false;
        }
        final int taskId = new Integer(args[2]);
        mSplitScreenOptional.ifPresent(split -> split.unpinTask(taskId));
        mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId));
        return true;
    }

    private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
        if (args.length < 3) {
            // First arguments are "WMShell" and command name.
            pw.println("Error: side stage position should be provided as arguments");
            return false;
        }
        final int position = new Integer(args[2]);
        mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position));
        return true;
    }

    private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) {
        if (args.length < 3) {
            // First arguments are "WMShell" and command name.
            pw.println("Error: side stage position should be provided as arguments");
            return false;
        }
        final Boolean visible = new Boolean(args[2]);

        mSplitScreenOptional.ifPresent(split -> split.setSideStageVisibility(visible));
        return true;
    }

@@ -172,9 +203,14 @@ public final class ShellCommandHandlerImpl {
        pw.println("  pair <taskId1> <taskId2>");
        pw.println("  unpair <taskId>");
        pw.println("    Pairs/unpairs tasks with given ids.");
        pw.println("  pinTask <taskId>");
        pw.println("  unpinTask <taskId>");
        pw.println("    Pin/Unpin a task with given id in split-screen mode.");
        pw.println("  moveToSideStage <taskId> <SideStagePosition>");
        pw.println("    Move a task with given id in split-screen mode.");
        pw.println("  removeFromSideStage <taskId>");
        pw.println("    Remove a task with given id in split-screen mode.");
        pw.println("  setSideStagePosition <SideStagePosition>");
        pw.println("    Sets the position of the side-stage.");
        pw.println("  setSideStageVisibility <true/false>");
        pw.println("    Show/hide side-stage.");
        return true;
    }

+3 −3
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ class SideStage extends StageTaskListener {
        super(taskOrganizer, displayId, callbacks, syncQueue);
    }

    public void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
    void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
            WindowContainerTransaction wct) {
        final WindowContainerToken rootToken = mRootTaskInfo.token;
        wct.setHidden(rootToken, false)
@@ -48,13 +48,13 @@ class SideStage extends StageTaskListener {
                .reorder(rootToken, true);
    }

    public boolean removeTask(int taskId, WindowContainerTransaction wct) {
    boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
        final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
        if (task == null) return false;

        wct.setHidden(mRootTaskInfo.token, true)
                .reorder(mRootTaskInfo.token, false)
                .reparent(task.token, null, false /* onTop */);
                .reparent(task.token, newParent, false /* onTop */);
        return true;
    }
}
+32 −7
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.splitscreen;

import android.annotation.IntDef;
import android.app.ActivityManager;

import androidx.annotation.NonNull;
@@ -29,13 +30,37 @@ import java.io.PrintWriter;
 */
@ExternalThread
public interface SplitScreen {
    /** Unpin a task in the side-stage of split-screen. */
    boolean pinTask(int taskId);
    /** Unpin a task in the side-stage of split-screen. */
    boolean pinTask(ActivityManager.RunningTaskInfo task);
    /** Unpin a task from the side-stage of split-screen. */
    boolean unpinTask(int taskId);
// TODO: Do we need show/hide side stage or is startActivity and sendToBack good enough?
    /**
     * Specifies that the side-stage is positioned at the top half of the screen if
     * in portrait mode or at the left half of the screen if in landscape mode.
     */
    int SIDE_STAGE_POSITION_TOP_OR_LEFT = 0;

    /**
     * Specifies that the side-stage is positioned at the bottom half of the screen if
     * in portrait mode or at the right half of the screen if in landscape mode.
     */
    int SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT = 1;

    @IntDef(prefix = { "SIDE_STAGE_POSITION_" }, value = {
            SIDE_STAGE_POSITION_TOP_OR_LEFT,
            SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT
    })
    @interface SideStagePosition {}

    /** @return {@code true} if split-screen is currently visible. */
    boolean isSplitScreenVisible();
    /** Moves a task in the side-stage of split-screen. */
    boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition);
    /** Moves a task in the side-stage of split-screen. */
    boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
            @SideStagePosition int sideStagePosition);
    /** Removes a task from the side-stage of split-screen. */
    boolean removeFromSideStage(int taskId);
    /** Sets the position of the side-stage. */
    void setSideStagePosition(@SideStagePosition int sideStagePosition);
    /** Hides the side-stage if it is currently visible. */
    void setSideStageVisibility(boolean visible);
    /** Dumps current status of split-screen. */
    void dump(@NonNull PrintWriter pw, String prefix);
    /** Called when the shell organizer has been registered. */
+22 −6
Original line number Diff line number Diff line
@@ -62,19 +62,35 @@ public class SplitScreenController implements SplitScreen {
    }

    @Override
    public boolean pinTask(int taskId) {
    public boolean isSplitScreenVisible() {
        return mStageCoordinator.isSplitScreenVisible();
    }

    @Override
    public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
        final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
        return task != null && pinTask(task);
        return task != null && moveToSideStage(task, sideStagePosition);
    }

    @Override
    public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
            @SideStagePosition int sideStagePosition) {
        return mStageCoordinator.moveToSideStage(task, sideStagePosition);
    }

    @Override
    public boolean removeFromSideStage(int taskId) {
        return mStageCoordinator.removeFromSideStage(taskId);
    }

    @Override
    public boolean pinTask(ActivityManager.RunningTaskInfo task) {
        return mStageCoordinator.pinTask(task);
    public void setSideStagePosition(@SideStagePosition int sideStagePosition) {
        mStageCoordinator.setSideStagePosition(sideStagePosition);
    }

    @Override
    public boolean unpinTask(int taskId) {
        return mStageCoordinator.unpinTask(taskId);
    public void setSideStageVisibility(boolean visible) {
        mStageCoordinator.setSideStageVisibility(visible);
    }

    @Override
+45 −14
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package com.android.wm.shell.splitscreen;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;

import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
@@ -58,6 +61,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
    private final StageListenerImpl mMainStageListener = new StageListenerImpl();
    private final SideStage mSideStage;
    private final StageListenerImpl mSideStageListener = new StageListenerImpl();
    private @SplitScreen.SideStagePosition int mSideStagePosition =
            SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;

    private final int mDisplayId;
    private SplitLayout mSplitLayout;
@@ -94,27 +99,49 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
        mRootTDAOrganizer.registerListener(displayId, this);
    }

    boolean pinTask(ActivityManager.RunningTaskInfo task) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
    boolean isSplitScreenVisible() {
        return mSideStageListener.mVisible && mMainStageListener.mVisible;
    }

    boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
            @SplitScreen.SideStagePosition int sideStagePosition) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        mSideStagePosition = sideStagePosition;
        mMainStage.activate(getMainStageBounds(), wct);
        mSideStage.addTask(task, getSideStageBounds(), wct);
        mTaskOrganizer.applyTransaction(wct);
        return true;
    }

    boolean unpinTask(int taskId) {
    boolean removeFromSideStage(int taskId) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();

        /**
         * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the
         * {@link SideStage} no longer has children.
         */
        final boolean result = mSideStage.removeTask(taskId, wct);
        final boolean result = mSideStage.removeTask(taskId,
                mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null,
                wct);
        mTaskOrganizer.applyTransaction(wct);
        return result;
    }

    void setSideStagePosition(@SplitScreen.SideStagePosition int sideStagePosition) {
        mSideStagePosition = sideStagePosition;
        if (mSideStageListener.mVisible) {
            onStageVisibilityChanged(mSideStageListener);
        }
    }

    void setSideStageVisibility(boolean visible) {
        if (!mSideStageListener.mVisible == visible) return;

        final WindowContainerTransaction wct = new WindowContainerTransaction();
        mSideStage.setVisibility(visible, wct);
        mTaskOrganizer.applyTransaction(wct);
    }

    private void onStageRootTaskVanished(StageListenerImpl stageListener) {
        if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -171,6 +198,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
                    t.hide(dividerLeash);
                }
            }

            if (sideStageVisible) {
                final Rect sideStageBounds = getSideStageBounds();
                t.show(sideStageLeash)
@@ -181,17 +209,18 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
            } else {
                t.hide(sideStageLeash);
            }

            if (mainStageVisible) {
                final Rect mainStageBounds = getMainStageBounds();
                t.show(mainStageLeash)
                        .setPosition(mainStageLeash,
                                mainStageBounds.left, mainStageBounds.top);
                t.show(mainStageLeash);
                if (sideStageVisible) {
                    t.setWindowCrop(mainStageLeash,
                    t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
                            .setWindowCrop(mainStageLeash,
                                    mainStageBounds.width(), mainStageBounds.height());
                } else {
                    // Clear window crop if side stage isn't visible.
                    t.setWindowCrop(mainStageLeash, null);
                    // Clear window crop and position if side stage isn't visible.
                    t.setPosition(mainStageLeash, 0, 0)
                            .setWindowCrop(mainStageLeash, null);
                }
            } else {
                t.hide(mainStageLeash);
@@ -204,7 +233,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            if (mSideStageListener.mHasChildren) {
                // Make sure the main stage is active.
                mMainStage.activate(mSplitLayout.getBounds1(), wct);
                mMainStage.activate(getMainStageBounds(), wct);
            } else {
                // The side stage no long has children so we can deactivate the main stage.
                mMainStage.deactivate(wct);
@@ -291,11 +320,13 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
    }

    private Rect getSideStageBounds() {
        return mSplitLayout.getBounds2();
        return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
                ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
    }

    private Rect getMainStageBounds() {
        return mSplitLayout.getBounds1();
        return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
                ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
    }

    @Override
Loading