From 6567742d10d19283114dd38b171f1569550a203d Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Mon, 8 Mar 2021 18:19:41 +0800 Subject: [PATCH] Move task to the bottom when force hidden Move a task to the bottom when the task was hidden to prevent the top focused activity unable to enter pip (while pausing) due to a invisible task remained on top. Move the task to top once being visible again Bug: 180405672 Test: atest TaskTests PinnedStackTests Change-Id: I038076c58cd41cc90039c9db5a295da5d5ee2cde --- .../core/java/com/android/server/wm/Task.java | 33 ++++++++++++++----- .../android/server/wm/TaskDisplayArea.java | 10 +----- .../src/com/android/server/wm/TaskTests.java | 12 +++++++ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d992a4591a220..04a254c2aabf7 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5245,17 +5245,29 @@ class Task extends WindowContainer { if (mForceHiddenFlags == newFlags) { return false; } + final boolean wasHidden = isForceHidden(); + final boolean wasVisible = isVisible(); mForceHiddenFlags = newFlags; - if (wasHidden != isForceHidden() && isTopActivityFocusable()) { - // The change in force-hidden state will change visibility without triggering a root - // task order change, so we should reset the preferred top focusable root task to ensure - // it's not used if a new activity is started from this task. - getDisplayArea().resetPreferredTopFocusableRootTaskIfNeeded(this); + final boolean nowHidden = isForceHidden(); + if (wasHidden != nowHidden) { + final String reason = "setForceHidden"; + if (wasVisible && nowHidden) { + // Move this visible task to back when the task is forced hidden + moveToBack(reason, null); + } else if (isAlwaysOnTop()) { + // Move this always-on-top task to front when no longer hidden + moveToFront(reason); + } } return true; } + @Override + public boolean isAlwaysOnTop() { + return !isForceHidden() && super.isAlwaysOnTop(); + } + /** * Returns whether this task is currently forced to be hidden for any reason. */ @@ -7482,17 +7494,22 @@ class Task extends WindowContainer { } public void setAlwaysOnTop(boolean alwaysOnTop) { - if (isAlwaysOnTop() == alwaysOnTop) { + // {@link #isAwaysonTop} overrides the original behavior which also evaluates if this + // task is force hidden, so super.isAlwaysOnTop() is used here to see whether the + // alwaysOnTop attributes should be updated. + if (super.isAlwaysOnTop() == alwaysOnTop) { return; } super.setAlwaysOnTop(alwaysOnTop); - final TaskDisplayArea taskDisplayArea = getDisplayArea(); // positionChildAtTop() must be called even when always on top gets turned off because we // need to make sure that the root task is moved from among always on top windows to // below other always on top windows. Since the position the root task should be inserted // into is calculated properly in {@link DisplayContent#getTopInsertPosition()} in both // cases, we can just request that the root task is put at top here. - taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */); + // Don't bother moving task to top if this task is force hidden and invisible to user. + if (!isForceHidden()) { + getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */); + } } void dismissPip() { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index ed92fd08bef5e..91aa48effe84a 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -393,7 +393,7 @@ final class TaskDisplayArea extends DisplayArea { final boolean moveToBottom = position <= 0; final int oldPosition = mChildren.indexOf(child); - if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) { + if (child.isAlwaysOnTop() && !moveToTop) { // This root task is always-on-top, override the default behavior. Slog.w(TAG_WM, "Ignoring move of always-on-top root task=" + this + " to bottom"); @@ -974,14 +974,6 @@ final class TaskDisplayArea extends DisplayArea { onRootTaskOrderChanged(rootTask); } - /** Reset the mPreferredTopFocusableRootTask if it is or below the given task. */ - void resetPreferredTopFocusableRootTaskIfNeeded(Task task) { - if (mPreferredTopFocusableRootTask != null - && mPreferredTopFocusableRootTask.compareTo(task) <= 0) { - mPreferredTopFocusableRootTask = null; - } - } - /** * Moves/reparents `task` to the back of whatever container the root home task is in. This is * for when we just want to move a task to "the back" vs. a specific place. The primary use-case diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index ecb8b607a1060..dca6b089d66b3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; import static com.google.common.truth.Truth.assertThat; @@ -277,4 +278,15 @@ public class TaskTests extends WindowTestsBase { // Orientation request from standard activity in multi window will not be handled. assertFalse(leafTask2.handlesOrientationChangeFromDescendant()); } + + @Test + public void testAlwaysOnTop() { + final Task task = createTaskStackOnDisplay(mDisplayContent); + task.setAlwaysOnTop(true); + task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + assertTrue(task.isAlwaysOnTop()); + + task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */); + assertFalse(task.isAlwaysOnTop()); + } }