Merge "Position stack at top when always on top flag is set"

This commit is contained in:
Kazuki Takise
2018-06-26 04:14:41 +00:00
committed by Android (Google) Code Review
8 changed files with 169 additions and 42 deletions

View File

@@ -488,7 +488,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
+ " mAppBounds=" + mAppBounds
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
+ " mActivityType=" + activityTypeToString(mActivityType)
+ " mAlwaysOnTop=" + activityTypeToString(mAlwaysOnTop)
+ " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+ "}";
}
@@ -652,4 +652,14 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
return String.valueOf(applicationType);
}
/** @hide */
public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) {
switch (alwaysOnTop) {
case ALWAYS_ON_TOP_UNDEFINED: return "undefined";
case ALWAYS_ON_TOP_ON: return "on";
case ALWAYS_ON_TOP_OFF: return "off";
}
return String.valueOf(alwaysOnTop);
}
}

View File

@@ -5279,6 +5279,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
public void setAlwaysOnTop(boolean alwaysOnTop) {
if (isAlwaysOnTop() == alwaysOnTop) {
return;
}
super.setAlwaysOnTop(alwaysOnTop);
final ActivityDisplay display = getDisplay();
// positionChildAtTop() must be called even when always on top gets turned off because we
// need to make sure that the stack is moved from among always on top windows to below other
// always on top windows. Since the position the stack should be inserted into is calculated
// properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
// request that the stack is put at top here.
display.positionChildAtTop(this);
}
void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
boolean setPause, String reason) {
if (!moveToFront) {

View File

@@ -3427,50 +3427,53 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @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;
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;
return POSITION_TOP;
}
// 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.
if (toTop && adding) {
final int topChildPosition = mChildren.size() - 1;
int belowAlwaysOnTopPosition = POSITION_BOTTOM;
for (int i = topChildPosition; i >= 0; --i) {
if (getStacks().get(i) != stack && !getStacks().get(i).isAlwaysOnTop()) {
belowAlwaysOnTopPosition = i;
break;
}
}
// The max possible position we can insert the stack at.
int maxPosition = POSITION_TOP;
// The min possible position we can insert the stack at.
int minPosition = POSITION_BOTTOM;
if (stack.isAlwaysOnTop()) {
if (hasPinnedStack()) {
// Always-on-top stacks go below the pinned stack.
maxPosition = getStacks().indexOf(mPinnedStack) - 1;
}
// Always-on-top stacks need to be above all other stacks.
minPosition = belowAlwaysOnTopPosition !=
POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
} else {
// Other stacks need to be below the always-on-top stacks.
maxPosition = belowAlwaysOnTopPosition !=
POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
}
int targetPosition = requestedPosition;
targetPosition = Math.min(targetPosition, maxPosition);
targetPosition = Math.max(targetPosition, minPosition);
int prevPosition = getStacks().indexOf(stack);
// The positions we calculated above (maxPosition, minPosition) do not take into
// consideration the following edge cases.
// 1) We need to adjust the position depending on the value "adding".
// 2) When we are moving a stack to another position, we also need to adjust the
// position depending on whether the stack is moving to a higher or lower position.
if ((targetPosition != requestedPosition) &&
(adding || targetPosition < prevPosition)) {
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;
}

View File

@@ -726,18 +726,33 @@ public class TaskStack extends WindowContainer<Task> implements
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
super.onConfigurationChanged(newParentConfig);
// Only need to update surface size here since the super method will handle updating
// surface position.
updateSurfaceSize(getPendingTransaction());
final int windowingMode = getWindowingMode();
final boolean isAlwaysOnTop = isAlwaysOnTop();
if (mDisplayContent == null || prevWindowingMode == windowingMode) {
if (mDisplayContent == null) {
return;
}
mDisplayContent.onStackWindowingModeChanged(this);
updateBoundsForWindowModeChange();
if (prevWindowingMode != windowingMode) {
mDisplayContent.onStackWindowingModeChanged(this);
updateBoundsForWindowModeChange();
}
if (prevIsAlwaysOnTop != isAlwaysOnTop) {
// positionStackAt(POSITION_TOP, this) must be called even when always on top gets
// turned off because we need to make sure that the stack is moved from among always on
// top windows to below other always on top windows. Since the position the stack should
// be inserted into is calculated properly in
// {@link DisplayContent#findPositionForStack()} in both cases, we can just request that
// the stack is put at top here.
mDisplayContent.positionStackAt(POSITION_TOP, this);
}
}
private void updateSurfaceBounds() {

View File

@@ -246,6 +246,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
if ((index < 0 && index != POSITION_BOTTOM)
|| (index > mChildren.size() && index != POSITION_TOP)) {
throw new IllegalArgumentException("addChild: invalid position=" + index
+ ", children number=" + mChildren.size());
}
if (index == POSITION_TOP) {
index = mChildren.size();
} else if (index == POSITION_BOTTOM) {
index = 0;
}
mChildren.add(index, child);
onChildAdded(child);

View File

@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
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_PRIMARY;
@@ -472,6 +473,42 @@ public class ActivityStackTests extends ActivityTestsBase {
assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack2);
}
@Test
public void testSetAlwaysOnTop() {
final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertTrue(mDefaultDisplay.getStackAbove(homeStack) == pinnedStack);
final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack.setAlwaysOnTop(true);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
// Ensure (non-pinned) always on top stack is put below pinned stack.
assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack) == pinnedStack);
final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
// Ensure non always on top stack is put below always on top stacks.
assertTrue(mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack) == alwaysOnTopStack);
final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack2.setAlwaysOnTop(true);
assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
// Ensure newly created always on top stack is placed above other all always on top stacks.
assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
alwaysOnTopStack2.setAlwaysOnTop(false);
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
}
@Test
public void testSplitScreenMoveToFront() throws Exception {
final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(

View File

@@ -413,6 +413,14 @@ public class DisplayContentTests extends WindowTestsBase {
// 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));
anotherAlwaysOnTopStack.setAlwaysOnTop(false);
mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
topPosition = mDisplayContent.getStacks().size() - 1;
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 2));
}
/**

View File

@@ -255,6 +255,33 @@ public class WindowContainerTests extends WindowTestsBase {
assertNull(controller.mContainer);
}
@Test
public void testAddChildByIndex() throws Exception {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
final TestWindowContainer root = builder.setLayer(0).build();
final TestWindowContainer child = root.addChildWindow();
final TestWindowContainer child2 = builder.setLayer(1).build();
final TestWindowContainer child3 = builder.setLayer(2).build();
final TestWindowContainer child4 = builder.setLayer(3).build();
// Test adding at top.
root.addChild(child2, POSITION_TOP);
assertEquals(child2, root.getChildAt(root.getChildrenCount() - 1));
// Test adding at bottom.
root.addChild(child3, POSITION_BOTTOM);
assertEquals(child3, root.getChildAt(0));
// Test adding in the middle.
root.addChild(child4, 1);
assertEquals(child3, root.getChildAt(0));
assertEquals(child4, root.getChildAt(1));
assertEquals(child, root.getChildAt(2));
assertEquals(child2, root.getChildAt(3));
}
@Test
public void testPositionChildAt() throws Exception {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();