diff options
| author | 2020-04-10 17:39:35 +0800 | |
|---|---|---|
| committer | 2020-04-17 17:23:17 +0800 | |
| commit | 6434be4cb4b0af8ca2084480560e83e7e47c1d1e (patch) | |
| tree | 7ad560766b08f2f389038a1be18f3e6fbf8da6e5 | |
| parent | 9f11dd4b649af1b984824eda53a9d0d6227f1257 (diff) | |
Adjusting focus starting from sibling rootable app tasks
The next rootable app task was not moved to front while
another rootable app task which did not have running activities
was on top. The task focus was not adjusted properly
because the focus was adjusted between root tasks only.
In that case, the next task info was delayed to dispatch
to organizer until the finishing activity on top was destroyed.
Bug: 152408408
Test: atest ActivityRecordTests
Change-Id: I4ce5bf4ee3a48155654cc300eb51da5e2262962e
4 files changed, 108 insertions, 61 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 521ffa50f869..1bf070f4bf3c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2536,23 +2536,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pauseKeyDispatchingLocked(); - // We are finishing the top focused activity and its stack has nothing to be focused so - // the next focusable stack should be focused. - if (mayAdjustTop - && (stack.topRunningActivity() == null || !stack.isTopActivityFocusable())) { - if (shouldAdjustGlobalFocus) { - // Move the entire hierarchy to top with updating global top resumed activity - // and focused application if needed. - stack.adjustFocusToNextFocusableStack("finish-top"); - } else { - // Only move the next stack to top in its task container. - final TaskDisplayArea taskDisplayArea = stack.getDisplayArea(); - next = taskDisplayArea.topRunningActivity(); - if (next != null) { - taskDisplayArea.positionStackAtTop(next.getRootTask(), - false /* includingParents */, "finish-display-top"); - } - } + // We are finishing the top focused activity and its task has nothing to be focused so + // the next focusable task should be focused. + if (mayAdjustTop && ((ActivityStack) task).topRunningActivity(true /* focusableOnly */) + == null) { + task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */, + shouldAdjustGlobalFocus); } finishActivityResults(resultCode, resultData); diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 5968eede0a27..265b5e26720e 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -856,7 +856,8 @@ class ActivityStack extends Task { /** Resume next focusable stack after reparenting to another display. */ void postReparent() { - adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */); + adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */, + true /* moveParentsToTop */); mRootWindowContainer.resumeFocusedStacksTopActivities(); // Update visibility of activities before notifying WM. This way it won't try to resize // windows that are no longer visible. @@ -2282,7 +2283,7 @@ class ActivityStack extends Task { final String reason = "noMoreActivities"; if (!isActivityTypeHome()) { - final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason); + final ActivityStack nextFocusedStack = adjustFocusToNextFocusableTask(reason); if (nextFocusedStack != null) { // Try to move focus to the next visible stack with a running activity if this // stack is not covering the entire screen or is on a secondary display with no home @@ -2491,48 +2492,6 @@ class ActivityStack extends Task { } /** - * Find next proper focusable stack and make it focused. - * @return The stack that now got the focus, {@code null} if none found. - */ - ActivityStack adjustFocusToNextFocusableStack(String reason) { - return adjustFocusToNextFocusableStack(reason, false /* allowFocusSelf */); - } - - /** - * Find next proper focusable stack and make it focused. - * @param allowFocusSelf Is the focus allowed to remain on the same stack. - * @return The stack that now got the focus, {@code null} if none found. - */ - private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) { - final ActivityStack stack = - mRootWindowContainer.getNextFocusableStack(this, !allowFocusSelf); - final String myReason = reason + " adjustFocusToNextFocusableStack"; - if (stack == null) { - return null; - } - - final ActivityRecord top = stack.topRunningActivity(); - - if (stack.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) { - // If we will be focusing on the home stack next and its current top activity isn't - // visible, then use the move the home stack task to top to make the activity visible. - stack.getDisplayArea().moveHomeActivityToTop(reason); - return stack; - } - - stack.moveToFront(myReason); - // Top display focused stack is changed, update top resumed activity if needed. - if (stack.mResumedActivity != null) { - mStackSupervisor.updateTopResumedActivityIfNeeded(); - // Set focused app directly because if the next focused activity is already resumed - // (e.g. the next top activity is on a different display), there won't have activity - // state change to update it. - mAtmService.setResumedActivityUncheckLocked(stack.mResumedActivity, reason); - } - return stack; - } - - /** * Finish the topmost activity that belongs to the crashed app. We may also finish the activity * that requested launch of the crashed one to prevent launch-crash loop. * @param app The app that crashed. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1ffa01c2bd66..7d43d0014853 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2572,6 +2572,80 @@ class Task extends WindowContainer<WindowContainer> { return currentCount[0]; } + /** + * Find next proper focusable stack and make it focused. + * @return The stack that now got the focus, {@code null} if none found. + */ + ActivityStack adjustFocusToNextFocusableTask(String reason) { + return adjustFocusToNextFocusableTask(reason, false /* allowFocusSelf */, + true /* moveParentsToTop */); + } + + /** Return the next focusable task by looking from the siblings and parent tasks */ + private Task getNextFocusableTask(boolean allowFocusSelf) { + final WindowContainer parent = getParent(); + if (parent == null) { + return null; + } + + final Task focusableTask = parent.getTask((task) -> (allowFocusSelf || task != this) + && ((ActivityStack) task).isFocusableAndVisible()); + if (focusableTask == null && parent.asTask() != null) { + return parent.asTask().getNextFocusableTask(allowFocusSelf); + } else { + return focusableTask; + } + } + + /** + * Find next proper focusable task and make it focused. + * @param reason The reason of making the adjustment. + * @param allowFocusSelf Is the focus allowed to remain on the same task. + * @param moveParentsToTop Whether to move parents to top while making the task focused. + * @return The root task that now got the focus, {@code null} if none found. + */ + ActivityStack adjustFocusToNextFocusableTask(String reason, boolean allowFocusSelf, + boolean moveParentsToTop) { + ActivityStack focusableTask = (ActivityStack) getNextFocusableTask(allowFocusSelf); + if (focusableTask == null) { + focusableTask = mRootWindowContainer.getNextFocusableStack((ActivityStack) this, + !allowFocusSelf); + } + if (focusableTask == null) { + return null; + } + + final String myReason = reason + " adjustFocusToNextFocusableStack"; + final ActivityRecord top = focusableTask.topRunningActivity(); + final ActivityStack rootTask = (ActivityStack) focusableTask.getRootTask(); + if (focusableTask.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) { + // If we will be focusing on the home stack next and its current top activity isn't + // visible, then use the move the home stack task to top to make the activity visible. + focusableTask.getDisplayArea().moveHomeActivityToTop(myReason); + return rootTask; + } + + if (!moveParentsToTop) { + // Only move the next stack to top in its task container. + WindowContainer parent = focusableTask.getParent(); + parent.positionChildAt(POSITION_TOP, focusableTask, false /* includingParents */); + return rootTask; + } + + // Move the entire hierarchy to top with updating global top resumed activity + // and focused application if needed. + focusableTask.moveToFront(myReason); + // Top display focused stack is changed, update top resumed activity if needed. + if (rootTask.mResumedActivity != null) { + mStackSupervisor.updateTopResumedActivityIfNeeded(); + // Set focused app directly because if the next focused activity is already resumed + // (e.g. the next top activity is on a different display), there won't have activity + // state change to update it. + mAtmService.setResumedActivityUncheckLocked(rootTask.mResumedActivity, reason); + } + return rootTask; + } + /** Calculate the minimum possible position for a task that can be shown to the user. * The minimum position will be above all other tasks that can't be shown. * @param minPosition The minimum position the caller is suggesting. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 5bbb4d9e712c..6a28918eab00 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -752,6 +752,31 @@ public class ActivityRecordTests extends ActivityTestsBase { } /** + * Verify that when finishing the top focused activity while root task was created by organizer, + * the stack order will be changed by adjusting focus. + */ + @Test + public void testFinishActivityIfPossible_adjustStackOrderOrganizedRoot() { + // Make mStack be a the root task that created by task organizer + mStack.mCreatedByOrganizer = true; + + // Have two tasks (topRootableTask and mTask) as the children of mStack. + ActivityRecord topActivity = new ActivityBuilder(mActivity.mAtmService) + .setCreateTask(true) + .setStack(mStack) + .build(); + ActivityStack topRootableTask = (ActivityStack) topActivity.getTask(); + topRootableTask.moveToFront("test"); + assertTrue(mStack.isTopStackInDisplayArea()); + + // Finish top activity and verify the next focusable rootable task has adjusted to top. + topActivity.setState(RESUMED, "test"); + topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, "test", + false /* oomAdj */); + assertEquals(mTask, mStack.getTopMostTask()); + } + + /** * Verify that resumed activity is paused due to finish request. */ @Test |