diff options
4 files changed, 95 insertions, 45 deletions
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ddef339caa5d..f7c3cea2eed6 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1390,24 +1390,6 @@ final class ActivityStack extends ConfigurationContainer { return null; } - ActivityStack getNextFocusableStackLocked() { - ArrayList<ActivityStack> stacks = mStacks; - final ActivityRecord parent = mActivityContainer.mParentActivity; - if (parent != null) { - stacks = parent.getStack().mStacks; - } - if (stacks != null) { - for (int i = stacks.size() - 1; i >= 0; --i) { - ActivityStack stack = stacks.get(i); - if (stack != this && stack.isFocusable() - && stack.getStackVisibilityLocked(null) != STACK_INVISIBLE) { - return stack; - } - } - } - return null; - } - /** Returns true if the stack contains a fullscreen task. */ private boolean hasFullscreenTask() { for (int i = mTaskHistory.size() - 1; i >= 0; --i) { @@ -2098,9 +2080,15 @@ final class ActivityStack extends ConfigurationContainer { return false; } - ActivityRecord parent = mActivityContainer.mParentActivity; - if ((parent != null && parent.state != ActivityState.RESUMED) || - !mActivityContainer.isAttachedLocked()) { + // Find the topmost activity in this stack that is not finishing. + final ActivityRecord next = topRunningActivityLocked(); + + final boolean hasRunningActivity = next != null; + + final ActivityRecord parent = mActivityContainer.mParentActivity; + final boolean isParentNotResumed = parent != null && parent.state != ActivityState.RESUMED; + if (hasRunningActivity + && (isParentNotResumed || !mActivityContainer.isAttachedLocked())) { // Do not resume this stack if its parent is not resumed. // TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st. return false; @@ -2108,33 +2096,14 @@ final class ActivityStack extends ConfigurationContainer { mStackSupervisor.cancelInitializingActivities(); - // Find the first activity that is not finishing. - final ActivityRecord next = topRunningActivityLocked(); - // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. final boolean userLeaving = mStackSupervisor.mUserLeaving; mStackSupervisor.mUserLeaving = false; - final TaskRecord prevTask = prev != null ? prev.task : null; - if (next == null) { - // There are no more activities! - final String reason = "noMoreActivities"; - if (!mFullscreen && adjustFocusToNextFocusableStackLocked(reason)) { - // Try to move focus to the next visible stack with a running activity if this - // stack is not covering the entire screen. - return mStackSupervisor.resumeFocusedStackTopActivityLocked( - mStackSupervisor.getFocusedStack(), prev, null); - } - - // Let's just start up the Launcher... - ActivityOptions.abort(options); - if (DEBUG_STATES) Slog.d(TAG_STATES, - "resumeTopActivityLocked: No more activities go home"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); - // Only resume home if on home display - return isOnHomeDisplay() && - mStackSupervisor.resumeHomeStackTask(prev, reason); + if (!hasRunningActivity) { + // There are no activities left in the stack, let's look somewhere else. + return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities"); } next.delayedResume = false; @@ -2152,6 +2121,7 @@ final class ActivityStack extends ConfigurationContainer { } final TaskRecord nextTask = next.task; + final TaskRecord prevTask = prev != null ? prev.task : null; if (prevTask != null && prevTask.getStack() == this && prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); @@ -2527,6 +2497,27 @@ final class ActivityStack extends ConfigurationContainer { return true; } + private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev, + ActivityOptions options, String reason) { + if ((!mFullscreen || !isOnHomeDisplay()) + && adjustFocusToNextFocusableStackLocked(reason)) { + // 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 + // stack). + return mStackSupervisor.resumeFocusedStackTopActivityLocked( + mStackSupervisor.getFocusedStack(), prev, null); + } + + // Let's just start up the Launcher... + ActivityOptions.abort(options); + if (DEBUG_STATES) Slog.d(TAG_STATES, + "resumeTopActivityInNextFocusableStack: " + reason + ", go home"); + if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); + // Only resume home if on home display + return isOnHomeDisplay() && + mStackSupervisor.resumeHomeStackTask(prev, reason); + } + private TaskRecord getNextTask(TaskRecord targetTask) { final int index = mTaskHistory.indexOf(targetTask); if (index >= 0) { @@ -3151,7 +3142,7 @@ final class ActivityStack extends ConfigurationContainer { } private boolean adjustFocusToNextFocusableStackLocked(String reason) { - final ActivityStack stack = getNextFocusableStackLocked(); + final ActivityStack stack = mStackSupervisor.getNextFocusableStackLocked(this); final String myReason = reason + " adjustFocusToNextFocusableStack"; if (stack == null) { return false; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8ab3ac39dd5b..5def3403dc56 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -455,6 +455,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); /** + * Temp storage for display ids sorted in focus order. + * Maps position to id. Using {@link SparseIntArray} instead of {@link ArrayList} because + * it's more efficient, as the number of displays is usually small. + */ + private SparseIntArray mTmpOrderedDisplayIds = new SparseIntArray(); + + /** * Used to keep track whether app visibilities got changed since the last pause. Useful to * determine whether to invoke the task stack change listener after pausing. */ @@ -637,7 +644,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) { if (!focusCandidate.isFocusable()) { // The focus candidate isn't focusable. Move focus to the top stack that is focusable. - focusCandidate = focusCandidate.getNextFocusableStackLocked(); + focusCandidate = getNextFocusableStackLocked(focusCandidate); } if (focusCandidate != mFocusedStack) { @@ -2019,6 +2026,35 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return mActivityDisplays.valueAt(DEFAULT_DISPLAY).mStacks; } + /** + * Get next focusable stack in the system. This will search across displays and stacks + * in last-focused order for a focusable and visible stack, different from the target stack. + * + * @param currentFocus The stack that previously had focus and thus needs to be ignored when + * searching for next candidate. + * @return Next focusable {@link ActivityStack}, null if not found. + */ + ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) { + mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds); + + for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) { + final int displayId = mTmpOrderedDisplayIds.get(i); + final List<ActivityStack> stacks = mActivityDisplays.get(displayId).mStacks; + if (stacks == null) { + continue; + } + for (int j = stacks.size() - 1; j >= 0; --j) { + final ActivityStack stack = stacks.get(j); + if (stack != currentFocus && stack.isFocusable() + && stack.getStackVisibilityLocked(null) != STACK_INVISIBLE) { + return stack; + } + } + } + + return null; + } + ActivityRecord getHomeActivity() { return getHomeActivityForUser(mCurrentUser); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 349740b0bd38..dc06d129c5bb 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -166,6 +166,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } /** + * Get an array with display ids ordered by focus priority - last items should be given + * focus first. Sparse array just maps position to displayId. + */ + void getDisplaysInFocusOrder(SparseIntArray displaysInFocusOrder) { + displaysInFocusOrder.clear(); + + final int size = mChildren.size(); + for (int i = 0; i < size; ++i) { + displaysInFocusOrder.put(i, mChildren.get(i).getDisplayId()); + } + } + + /** * Retrieve the DisplayContent for the specified displayId. Will create a new DisplayContent if * there is a Display for the displayId. * diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d5aa01b6a0b7..38cb54320a1b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6638,6 +6638,16 @@ public class WindowManagerService extends IWindowManager.Stub displayInfo.overscanRight, displayInfo.overscanBottom); } + /** + * Get an array with display ids ordered by focus priority - last items should be given + * focus first. Sparse array just maps position to displayId. + */ + public void getDisplaysInFocusOrder(SparseIntArray displaysInFocusOrder) { + synchronized(mWindowMap) { + mRoot.getDisplaysInFocusOrder(displaysInFocusOrder); + } + } + @Override public void setOverscan(int displayId, int left, int top, int right, int bottom) { if (mContext.checkCallingOrSelfPermission( |