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

Commit 926aade0 authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Introducing split-screen windowing modes.

WINDOWING_MODE_SPLIT_SCREEN_PRIMARY is what used to be the docked
windowing mode and is used to indicated the primary container
dirving the split-screen windowing mode.
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY is the windowing mode of any
container to the side/adjacent to the primary split-screen container.
For example, any container that was in fullscreen mode and that should
now be adjacent to the primary split-screen container will.

Test: go/wm-smoke
Test: WM Unit tests via TreeHugger
Change-Id: Idc8560073c613c708cb40ba8449641a6be11d9f1
parent 6cbbc9a0
Loading
Loading
Loading
Loading
+7 −25
Original line number Diff line number Diff line
@@ -20,10 +20,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_DOCKED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import android.Manifest;
@@ -738,28 +739,6 @@ public class ActivityManager {
                    || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
        }

        /**
         * Returns true if Stack size is affected by the docked stack changing size.
         * @hide
         */
        // TODO: Figure-out a way to remove.
        public static boolean isResizeableByDockedStack(int stackId) {
            return isStaticStack(stackId) && stackId != DOCKED_STACK_ID
                    && stackId != PINNED_STACK_ID && stackId != ASSISTANT_STACK_ID;
        }

        /**
         * Returns true if the size of tasks in the input stack are affected by the docked stack
         * changing size.
         * @hide
         */
        // TODO: What is the difference between this method and the one above??
        public static boolean isTaskResizeableByDockedStack(int stackId) {
            return isStaticStack(stackId) && stackId != FREEFORM_WORKSPACE_STACK_ID
                    && stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID
                    && stackId != ASSISTANT_STACK_ID;
        }

        /**
         * Returns true if the input stack is affected by drag resizing.
         * @hide
@@ -879,12 +858,15 @@ public class ActivityManager {
        /** Returns the windowing mode that should be used for this input stack id.
         * @hide */
        // TODO: To be removed once we are not using stack id for stuff...
        public static int getWindowingModeForStackId(int stackId) {
        public static int getWindowingModeForStackId(int stackId, boolean inSplitScreenMode) {
            final int windowingMode;
            switch (stackId) {
                case FULLSCREEN_WORKSPACE_STACK_ID:
                case HOME_STACK_ID:
                case RECENTS_STACK_ID:
                    windowingMode = inSplitScreenMode
                            ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN;
                    break;
                case ASSISTANT_STACK_ID:
                    windowingMode = WINDOWING_MODE_FULLSCREEN;
                    break;
@@ -892,7 +874,7 @@ public class ActivityManager {
                    windowingMode = WINDOWING_MODE_PINNED;
                    break;
                case DOCKED_STACK_ID:
                    windowingMode = WINDOWING_MODE_DOCKED;
                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
                    break;
                case FREEFORM_WORKSPACE_STACK_ID:
                    windowingMode = WINDOWING_MODE_FREEFORM;
+26 −5
Original line number Diff line number Diff line
@@ -57,19 +57,26 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
    /** Always on-top (always visible). of other siblings in its parent container.
     * @hide */
    public static final int WINDOWING_MODE_PINNED = 2;
    /** Occupies a dedicated region of the screen or its parent container.
    /** The primary container driving the screen to be in split-screen mode.
     * @hide */
    public static final int WINDOWING_MODE_DOCKED = 3;
    public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3;
    /**
     * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in
     * split-screen mode.
     * @hide
     */
    public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4;
    /** Can be freely resized within its parent container.
     * @hide */
    public static final int WINDOWING_MODE_FREEFORM = 4;
    public static final int WINDOWING_MODE_FREEFORM = 5;

    /** @hide */
    @IntDef({
            WINDOWING_MODE_UNDEFINED,
            WINDOWING_MODE_FULLSCREEN,
            WINDOWING_MODE_PINNED,
            WINDOWING_MODE_DOCKED,
            WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
            WINDOWING_MODE_FREEFORM,
    })
    public @interface WindowingMode {}
@@ -469,12 +476,26 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        return mWindowingMode == WINDOWING_MODE_PINNED;
    }

    /**
     * Returns true if this container can be put in either
     * {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
     * {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state.
     * @hide
     */
    public boolean supportSplitScreenWindowingMode() {
        if (mActivityType == ACTIVITY_TYPE_ASSISTANT) {
            return false;
        }
        return mWindowingMode != WINDOWING_MODE_FREEFORM && mWindowingMode != WINDOWING_MODE_PINNED;
    }

    private static String windowingModeToString(@WindowingMode int windowingMode) {
        switch (windowingMode) {
            case WINDOWING_MODE_UNDEFINED: return "undefined";
            case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
            case WINDOWING_MODE_PINNED: return "pinned";
            case WINDOWING_MODE_DOCKED: return "docked";
            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary";
            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary";
            case WINDOWING_MODE_FREEFORM: return "freeform";
        }
        return String.valueOf(windowingMode);
+24 −19
Original line number Diff line number Diff line
@@ -6,10 +6,12 @@ import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
import static android.app.WindowConfiguration.WINDOWING_MODE_DOCKED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CALLING_PACKAGE_NAME;
@@ -111,31 +113,34 @@ class ActivityMetricsLogger {
        }
        mLastLogTimeSecs = now;

        ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
        if (stack != null && stack.shouldBeVisible(null) != STACK_INVISIBLE) {
            mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
        mWindowState = WINDOW_STATE_INVALID;
        ActivityStack stack = mSupervisor.getFocusedStack();
        if (stack.isActivityTypeAssistant()) {
            mWindowState = WINDOW_STATE_ASSISTANT;
            return;
        }
        mWindowState = WINDOW_STATE_INVALID;
        stack = mSupervisor.getFocusedStack();

        int windowingMode = stack.getWindowingMode();
        if (windowingMode == WINDOWING_MODE_PINNED) {
            stack = mSupervisor.findStackBehind(stack);
            windowingMode = stack.getWindowingMode();
        }
        if (StackId.isHomeOrRecentsStack(stack.mStackId)
                || windowingMode == WINDOWING_MODE_FULLSCREEN) {
        switch (windowingMode) {
            case WINDOWING_MODE_FULLSCREEN:
                mWindowState = WINDOW_STATE_STANDARD;
        } else if (windowingMode == WINDOWING_MODE_DOCKED) {
            Slog.wtf(TAG, "Docked stack shouldn't be the focused stack, because it reported not"
                    + " being visible.");
            mWindowState = WINDOW_STATE_INVALID;
        } else if (windowingMode == WINDOWING_MODE_FREEFORM) {
                break;
            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
                mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
                break;
            case WINDOW_STATE_FREEFORM:
                mWindowState = WINDOW_STATE_FREEFORM;
        } else if (stack.mStackId == ASSISTANT_STACK_ID) {
            mWindowState = WINDOW_STATE_ASSISTANT;
        } else if (StackId.isStaticStack(stack.mStackId)) {
            throw new IllegalStateException("Unknown stack=" + stack);
                break;
            default:
                if (windowingMode != WINDOWING_MODE_UNDEFINED) {
                    throw new IllegalStateException("Unknown windowing mode for stack=" + stack
                            + " windowingMode=" + windowingMode);
                }
        }
    }

+2 −1
Original line number Diff line number Diff line
@@ -482,7 +482,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
    // TODO: Not needed once we are no longer using stack ids as the override config. can be passed
    // in.
    private void updateOverrideConfiguration() {
        final int windowingMode = getWindowingModeForStackId(mStackId);
        final int windowingMode = getWindowingModeForStackId(
                mStackId, mStackSupervisor.getStack(DOCKED_STACK_ID) != null);
        if (windowingMode != WINDOWING_MODE_UNDEFINED) {
            setWindowingMode(windowingMode);
        }
+44 −30
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCRE
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@@ -2279,15 +2281,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        return null;
    }

    /**
     * Returns if a stack should be treated as if it's docked. Returns true if the stack is
     * the docked stack itself, or if it's side-by-side to the docked stack.
     */
    boolean isStackDockedInEffect(int stackId) {
        return stackId == DOCKED_STACK_ID ||
                (StackId.isResizeableByDockedStack(stackId) && getStack(DOCKED_STACK_ID) != null);
    }

    void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
            boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
        if (stackId == DOCKED_STACK_ID) {
@@ -2301,9 +2294,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
            return;
        }

        final boolean splitScreenActive = getStack(DOCKED_STACK_ID) != null;
        if (!allowResizeInDockedMode
                && !stack.getWindowConfiguration().tasksAreFloating()
                && getStack(DOCKED_STACK_ID) != null) {
                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
            // If the docked stack exists, don't resize non-floating stacks independently of the
            // size computed from the docked stack size (otherwise they will be out of sync)
            return;
@@ -2312,6 +2305,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
        mWindowManager.deferSurfaceLayout();
        try {
            if (stack.supportSplitScreenWindowingMode()) {
                if (bounds == null && stack.inSplitScreenWindowingMode()) {
                    // null bounds = fullscreen windowing mode...at least for now.
                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
                } else if (splitScreenActive) {
                    // If we are in split-screen mode and this stack support split-screen, then
                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
                }
            }
            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
            if (!deferResume) {
                stack.ensureVisibleActivitiesConfigurationLocked(
@@ -2323,14 +2326,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        }
    }

    void deferUpdateBounds(int stackId) {
    private void deferUpdateBounds(int stackId) {
        final ActivityStack stack = getStack(stackId);
        if (stack != null) {
            stack.deferUpdateBounds();
        }
    }

    void continueUpdateBounds(int stackId) {
    private void continueUpdateBounds(int stackId) {
        final ActivityStack stack = getStack(stackId);
        if (stack != null) {
            stack.continueUpdateBounds();
@@ -2365,13 +2368,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                // which is dismissing the docked stack, so resize all other stacks to
                // fullscreen here already so we don't end up with resize trashing.
                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
                    if (StackId.isResizeableByDockedStack(i)) {
                        ActivityStack otherStack = getStack(i);
                        if (otherStack != null) {
                            resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
                                    true /* allowResizeInDockedMode */, DEFER_RESUME);
                    final ActivityStack otherStack = getStack(i);
                    if (otherStack == null) {
                        continue;
                    }
                    if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                        continue;
                    }
                    resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
                            true /* allowResizeInDockedMode */, DEFER_RESUME);
                }

                // Also disable docked stack resizing since we have manually adjusted the
@@ -2485,8 +2490,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                // screen controls and is also the same for all stacks.
                final Rect otherTaskRect = new Rect();
                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
                    if (i == DOCKED_STACK_ID) {
                        continue;
                    }
                    final ActivityStack current = getStack(i);
                    if (current != null && StackId.isResizeableByDockedStack(i)) {
                    if (current == null || !current.supportSplitScreenWindowingMode()) {
                        continue;
                    }
                    // Need to set windowing mode here before we try to get the dock bounds.
                    current.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
                    current.getStackDockedModeBounds(
                            tempOtherTaskBounds /* currentTempTaskBounds */,
                            tempRect /* outStackBounds */,
@@ -2498,7 +2510,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                            true /* allowResizeInDockedMode */, deferResume);
                }
            }
            }
            if (!deferResume) {
                stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
            }
@@ -4023,7 +4034,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        final boolean isSecondaryDisplayPreferred =
                (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY)
                || StackId.isDynamicStack(preferredStackId);
        if (((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
        final ActivityStack actualStack = getStack(actualStackId);
        final boolean inSplitScreenMode = actualStack != null
                && actualStack.inSplitScreenWindowingMode();
        if (((!inSplitScreenMode && preferredStackId != DOCKED_STACK_ID)
                && !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) {
            return;
        }
Loading