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

Commit cd5dcb8b authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Always place stacks below pinned stack

When positioning or adding stacks we must make sure
that no stack is above pinned stack.

Bug: 34049027
Test: runtest frameworks-services -c com.android.server.wm.TaskStackContainersTests
Change-Id: Ic92a4e07e9cde42deed53912ac1419fde4ab2f08
parent afa10b80
Loading
Loading
Loading
Loading
+41 −14
Original line number Diff line number Diff line
@@ -2498,19 +2498,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        }

        private void addChild(TaskStack stack, boolean toTop) {
            int addIndex = toTop ? mChildren.size() : 0;

            if (toTop
                    && mService.isStackVisibleLocked(PINNED_STACK_ID)
                    && stack.mStackId != PINNED_STACK_ID) {
                // The pinned stack is always the top most stack (always-on-top) when it is visible.
                // So, stack is moved just below the pinned stack.
                addIndex--;
                TaskStack topStack = mChildren.get(addIndex);
                if (topStack.mStackId != PINNED_STACK_ID) {
                    throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
                }
            }
            final int addIndex = findPositionForStack(toTop ? mChildren.size() : 0, stack,
                    true /* adding */);
            addChild(stack, addIndex);
            setLayoutNeeded();
        }
@@ -2528,7 +2517,45 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
                return;
            }

            super.positionChildAt(position, child, includingParents);
            final int targetPosition = findPositionForStack(position, child, false /* adding */);
            super.positionChildAt(targetPosition, child, includingParents);

            setLayoutNeeded();
        }

        /**
         * When stack is added or repositioned, find a proper position for it.
         * This will make sure that pinned stack always stays on top.
         * @param requestedPosition Position requested by caller.
         * @param stack Stack to be added or positioned.
         * @param adding Flag indicates whether we're adding a new stack or positioning an existing.
         * @return The proper position for the stack.
         */
        private int findPositionForStack(int requestedPosition, TaskStack stack, boolean adding) {
            final int topChildPosition = mChildren.size() - 1;
            boolean toTop = requestedPosition == POSITION_TOP;
            toTop |= adding ? requestedPosition >= topChildPosition + 1
                    : requestedPosition >= topChildPosition;
            int targetPosition = requestedPosition;

            if (toTop
                    && mService.isStackVisibleLocked(PINNED_STACK_ID)
                    && stack.mStackId != PINNED_STACK_ID) {
                // The pinned stack is always the top most stack (always-on-top) when it is visible.
                TaskStack topStack = mChildren.get(topChildPosition);
                if (topStack.mStackId != PINNED_STACK_ID) {
                    throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
                }

                // 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
                // stack, because WindowContainer#positionAt() first removes element and then adds it
                // to specified place.
                targetPosition = adding ? topChildPosition : topChildPosition - 1;
            }

            return targetPosition;
        }

        @Override
+1 −1
Original line number Diff line number Diff line
@@ -434,7 +434,7 @@ class WindowContainer<E extends WindowContainer> implements Comparable<WindowCon
        // TODO: Will this be more correct if it checks the visibility of its parents?
        // It depends...For example, Tasks and Stacks are only visible if there children are visible
        // but, WindowState are not visible if there parent are not visible. Maybe have the
        // container specify which direction to treverse for for visibility?
        // container specify which direction to traverse for visibility?
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowContainer wc = mChildren.get(i);
            if (wc.isVisible()) {
+42 −2
Original line number Diff line number Diff line
@@ -47,14 +47,15 @@ public class TaskStackContainersTests extends WindowTestsBase {
        // Test that always-on-top stack can't be moved to position other than top.
        final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent);
        final TaskStack stack2 = createTaskStackOnDisplay(sDisplayContent);
        sDisplayContent.addStackToDisplay(PINNED_STACK_ID, true);
        final TaskStack pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID);
        final TaskStack pinnedStack = addPinnedStack();

        final WindowContainer taskStackContainer = stack1.getParent();

        final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
        final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack);
        assertGreaterThan(pinnedStackPos, stack2Pos);
        assertGreaterThan(stack2Pos, stack1Pos);

        taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedStack, false);
        assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
@@ -66,4 +67,43 @@ public class TaskStackContainersTests extends WindowTestsBase {
        assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
    }
    @Test
    public void testStackPositionBelowPinnedStack() throws Exception {
        // Test that no stack can be above pinned stack.
        final TaskStack pinnedStack = addPinnedStack();
        final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent);

        final WindowContainer taskStackContainer = stack1.getParent();

        final int stackPos = taskStackContainer.mChildren.indexOf(stack1);
        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack);
        assertGreaterThan(pinnedStackPos, stackPos);

        taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false);
        assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);

        taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false);
        assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
    }

    private TaskStack addPinnedStack() {
        TaskStack pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID);
        if (pinnedStack == null) {
            sDisplayContent.addStackToDisplay(PINNED_STACK_ID, true);
            pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID);
        }

        if (!pinnedStack.isVisible()) {
            // Stack should contain visible app window to be considered visible.
            final Task pinnedTask = createTaskInStack(pinnedStack, 0 /* userId */);
            assertFalse(pinnedStack.isVisible());
            final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent);
            pinnedTask.addChild(pinnedApp, 0 /* addPos */);
            assertTrue(pinnedStack.isVisible());
        }

        return pinnedStack;
    }
}