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

Commit 661e012f authored by Kazuki Takise's avatar Kazuki Takise Committed by Android (Google) Code Review
Browse files

Merge "Add always on top feature support"

parents 92f2661c 148d00ac
Loading
Loading
Loading
Loading
+47 −4
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
    /** The current windowing mode of the configuration. */
    private @WindowingMode int mWindowingMode;

    private int mFlags;

    /** Indicates that this window should always be on top of the other windows. */
    private static final int PFLAG_ALWAYS_ON_TOP = 1 << 0;

    /** Windowing mode is currently not defined. */
    public static final int WINDOWING_MODE_UNDEFINED = 0;
    /** Occupies the full area of the screen or the parent container. */
@@ -136,13 +141,16 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
    /** Bit that indicates that the {@link #mActivityType} changed.
     * @hide */
    public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;

    /** Bit that indicates that the {@link #mFlags} changed.
     * @hide */
    public static final int WINDOW_CONFIG_FLAGS = 1 << 4;
    /** @hide */
    @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
            WINDOW_CONFIG_BOUNDS,
            WINDOW_CONFIG_APP_BOUNDS,
            WINDOW_CONFIG_WINDOWING_MODE,
            WINDOW_CONFIG_ACTIVITY_TYPE
            WINDOW_CONFIG_ACTIVITY_TYPE,
            WINDOW_CONFIG_FLAGS
    })
    public @interface WindowConfig {}

@@ -168,6 +176,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        dest.writeParcelable(mAppBounds, flags);
        dest.writeInt(mWindowingMode);
        dest.writeInt(mActivityType);
        dest.writeInt(mFlags);
    }

    private void readFromParcel(Parcel source) {
@@ -175,6 +184,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        mAppBounds = source.readParcelable(Rect.class.getClassLoader());
        mWindowingMode = source.readInt();
        mActivityType = source.readInt();
        mFlags = source.readInt();
    }

    @Override
@@ -222,6 +232,23 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
    }

    private void setFlags(int flags) {
        mFlags = flags;
    }

    /**
     * Sets whether this window should be always on top.
     * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
     * @hide
     */
    public void setAlwaysOnTop(boolean alwaysOnTop) {
        if (alwaysOnTop) {
            mFlags |= PFLAG_ALWAYS_ON_TOP;
        } else {
            mFlags &= ~PFLAG_ALWAYS_ON_TOP;
        }
    }

    /**
     * @see #setAppBounds(Rect)
     * @see #getAppBounds()
@@ -281,6 +308,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        setAppBounds(other.mAppBounds);
        setWindowingMode(other.mWindowingMode);
        setActivityType(other.mActivityType);
        setFlags(other.mFlags);
    }

    /** Set this object to completely undefined.
@@ -295,6 +323,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        setBounds(null);
        setWindowingMode(WINDOWING_MODE_UNDEFINED);
        setActivityType(ACTIVITY_TYPE_UNDEFINED);
        setFlags(0);
    }

    /**
@@ -312,6 +341,10 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
            changed |= WINDOW_CONFIG_BOUNDS;
            setBounds(delta.mBounds);
        }
        if (delta.mFlags != mFlags) {
            changed |= WINDOW_CONFIG_FLAGS;
            setFlags(delta.mFlags);
        }
        if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) {
            changed |= WINDOW_CONFIG_APP_BOUNDS;
            setAppBounds(delta.mAppBounds);
@@ -347,6 +380,10 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
            changes |= WINDOW_CONFIG_BOUNDS;
        }

        if (mFlags != other.mFlags) {
            changes |= WINDOW_CONFIG_FLAGS;
        }

        // Make sure that one of the values is not null and that they are not equal.
        if ((compareUndefined || other.mAppBounds != null)
                && mAppBounds != other.mAppBounds
@@ -399,6 +436,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        n = mActivityType - that.mActivityType;
        if (n != 0) return n;

        n = mFlags - that.mFlags;
        if (n != 0) return n;

        // if (n != 0) return n;
        return n;
    }
@@ -425,6 +465,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu

        result = 31 * result + mWindowingMode;
        result = 31 * result + mActivityType;
        result = 31 * result + mFlags;
        return result;
    }

@@ -434,7 +475,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
        return "{ mBounds=" + mBounds
                + " mAppBounds=" + mAppBounds
                + " mWindowingMode=" + windowingModeToString(mWindowingMode)
                + " mActivityType=" + activityTypeToString(mActivityType) + "}";
                + " mActivityType=" + activityTypeToString(mActivityType)
                + " mFlags=0x" + Integer.toHexString(mFlags)
                + "}";
    }

    /**
@@ -520,7 +563,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
     * @hide
     */
    public boolean isAlwaysOnTop() {
        return mWindowingMode == WINDOWING_MODE_PINNED;
        return mWindowingMode == WINDOWING_MODE_PINNED || (mFlags & PFLAG_ALWAYS_ON_TOP) != 0;
    }

    /**
+15 −5
Original line number Diff line number Diff line
@@ -173,12 +173,22 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>

    private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
        int position = mStacks.size();
        if (position > 0) {
            final ActivityStack topStack = mStacks.get(position - 1);
            if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) {
                // If the top stack is always on top, we move this stack just below it.
                position--;
        if (stack.inPinnedWindowingMode()) {
            // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
            // just return the candidate position.
            return Math.min(position, candidatePosition);
        }
        while (position > 0) {
            final ActivityStack targetStack = mStacks.get(position - 1);
            if (!targetStack.isAlwaysOnTop()) {
                // We reached a stack that isn't always-on-top.
                break;
            }
            if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
                // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
                break;
            }
            position--;
        }
        return Math.min(position, candidatePosition);
    }
+13 −1
Original line number Diff line number Diff line
@@ -302,6 +302,18 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
        onOverrideConfigurationChanged(mTmpConfig);
    }

    /** Sets the always on top flag for this configuration container.
     *  When you call this function, make sure that the following functions are called as well to
     *  keep proper z-order.
     *  - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
     *  - {@Link ActivityDisplay#positionChildAtTop(ActivityStack)};
     * */
    public void setAlwaysOnTop(boolean alwaysOnTop) {
        mTmpConfig.setTo(getOverrideConfiguration());
        mTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
        onOverrideConfigurationChanged(mTmpConfig);
    }

    /**
     * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
     * with another activity.
@@ -513,7 +525,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
        return toString();
    }

    boolean isAlwaysOnTop() {
    public boolean isAlwaysOnTop() {
        return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
    }

+41 −13
Original line number Diff line number Diff line
@@ -1565,6 +1565,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        return mTaskStackContainers.getStack(windowingMode, activityType);
    }

    @VisibleForTesting
    WindowList<TaskStack> getStacks() {
        return mTaskStackContainers.mChildren;
    }

    @VisibleForTesting
    TaskStack getTopStack() {
        return mTaskStackContainers.getTopStack();
@@ -3426,21 +3431,44 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            boolean toTop = requestedPosition == POSITION_TOP;
            toTop |= adding ? requestedPosition >= topChildPosition + 1
                    : requestedPosition >= topChildPosition;
            int targetPosition = requestedPosition;

            if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED && hasPinnedStack()) {
                // The pinned stack is always the top most stack (always-on-top) when it is present.
                TaskStack topStack = mChildren.get(topChildPosition);
                if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
                    throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
            if (stack.inPinnedWindowingMode()) {
                // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
                // just return the candidate position.
                return requestedPosition;
            }

                // So, stack is moved just below the pinned stack.
                // When we're adding a new stack the target is the current pinned stack position.
                // When we're positioning an existing stack the target is the position below pinned
            // We might call mChildren.get() with targetPosition below, but targetPosition might be
            // POSITION_TOP (INTEGER_MAX). We need to adjust the value to the actual index in the
            // array.
            int targetPosition = toTop ? topChildPosition : requestedPosition;
            // Note that the index we should return varies depending on the value of adding.
            // When we're adding a new stack the index is the current target position.
            // When we're positioning an existing stack the index is the position below the target
            // stack, because WindowContainer#positionAt() first removes element and then adds
            // it to specified place.
                targetPosition = adding ? topChildPosition : topChildPosition - 1;
            if (toTop && adding) {
                targetPosition++;
            }

            // Note we might have multiple always on top windows.
            while (targetPosition >= 0) {
                int adjustedTargetStackId = adding ? targetPosition - 1 : targetPosition;
                if (adjustedTargetStackId < 0 || adjustedTargetStackId > topChildPosition) {
                    break;
                }
                TaskStack targetStack = mChildren.get(adjustedTargetStackId);
                if (!targetStack.isAlwaysOnTop()) {
                    // We reached a stack that isn't always-on-top.
                    break;
                }
                if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
                    // Always on-top non-pinned windowing mode stacks can go anywhere below pinned
                    // stack.
                    break;
                }
                // We go one level down, looking for the place on which the new stack can be put.
                targetPosition--;
            }

            return targetPosition;
+24 −9
Original line number Diff line number Diff line
@@ -380,21 +380,36 @@ public class DisplayContentTests extends WindowTestsBase {
    }

    /**
     * This test enforces that the pinned stack is always kept as the top stack.
     * This test enforces that alwaysOnTop stack is placed at proper position.
     */
    @Test
    public void testPinnedStackLocation() {
    public void testAlwaysOnTopStackLocation() {
        final TaskStack alwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
        alwaysOnTopStack.setAlwaysOnTop(true);
        mDisplayContent.positionStackAt(POSITION_TOP, alwaysOnTopStack);
        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
        assertEquals(alwaysOnTopStack, mDisplayContent.getTopStack());

        final TaskStack pinnedStack = createStackControllerOnStackOnDisplay(
                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
        // Ensure that the pinned stack is the top stack
        assertEquals(pinnedStack, mDisplayContent.getPinnedStack());
        assertEquals(pinnedStack, mDisplayContent.getTopStack());
        // By default, this should try to create a new stack on top
        final TaskStack otherStack = createTaskStackOnDisplay(mDisplayContent);
        // Ensure that the other stack is on the display.
        assertEquals(mDisplayContent, otherStack.getDisplayContent());
        // Ensure that the pinned stack is still on top
        assertEquals(pinnedStack, mDisplayContent.getTopStack());

        final TaskStack anotherAlwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
        anotherAlwaysOnTopStack.setAlwaysOnTop(true);
        mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack);
        assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
        int topPosition = mDisplayContent.getStacks().size() - 1;
        // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
        // existing alwaysOnTop stack.
        assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));

        final TaskStack nonAlwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
        assertEquals(mDisplayContent, nonAlwaysOnTopStack.getDisplayContent());
        topPosition = mDisplayContent.getStacks().size() - 1;
        // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
        // existing other non-alwaysOnTop stacks.
        assertEquals(nonAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 3));
    }

    /**