diff options
| author | 2015-08-18 21:23:46 +0000 | |
|---|---|---|
| committer | 2015-08-18 21:23:46 +0000 | |
| commit | 339e4155fb0ff1667bd339b509a1503a348c547c (patch) | |
| tree | 9a2c93d0fe1fd35fa43ed76b00cf7008a2017a16 | |
| parent | 06f5e07a5477056977dde5ece13431e6626684c1 (diff) | |
| parent | b34a7ad1af54132b6b046ab8f768e0ffb81cf581 (diff) | |
Merge "Added support for docked stack"
10 files changed, 354 insertions, 59 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 8c25c58e4a71..a86c08d18aa5 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -429,10 +429,16 @@ public class ActivityManager { public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1; /** + * ID of stack that occupies a dedicated region of the screen. + * @hide + */ + public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1; + + /** * Last static stack stack ID. * @hide */ - public static final int LAST_STATIC_STACK_ID = FREEFORM_WORKSPACE_STACK_ID; + public static final int LAST_STATIC_STACK_ID = DOCKED_STACK_ID; /** * Start of ID range used by stacks that are created dynamically. diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 5f33344236c5..b003b31b20c2 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -3528,12 +3528,12 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override - public void resizeStack(int stackBoxId, Rect r) throws RemoteException + public void resizeStack(int stackId, Rect r) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - data.writeInt(stackBoxId); + data.writeInt(stackId); r.writeToParcel(data, 0); mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0); reply.readException(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d54930f46bfa..f3ea1c46af42 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19,6 +19,7 @@ package com.android.server.am; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.app.ActivityManager.DOCKED_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.util.XmlUtils.readBooleanAttribute; @@ -29,6 +30,7 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; import static com.android.server.am.ActivityManagerDebugConfig.*; +import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; @@ -8653,7 +8655,11 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.e(TAG, "setActivityBounds: No TaskRecord for the ActivityRecord r=" + r); return; } - mStackSupervisor.resizeTaskLocked(task, bounds); + if (task.stack != null && task.stack.mStackId == DOCKED_STACK_ID) { + mStackSupervisor.resizeStackLocked(task.stack.mStackId, bounds); + } else { + mStackSupervisor.resizeTaskLocked(task, bounds); + } } } finally { Binder.restoreCallingIdentity(ident); @@ -9010,7 +9016,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void moveActivityToStack(IBinder token, int stackId) throws RemoteException { if (stackId == HOME_STACK_ID) { throw new IllegalArgumentException( - "moveTaskToStack: Attempt to move token " + token + " to home stack"); + "moveActivityToStack: Attempt to move token " + token + " to home stack"); } synchronized (this) { long ident = Binder.clearCallingIdentity(); @@ -9022,13 +9028,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r + " to stackId=" + stackId); - mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP); - if (mFocusedActivity != r) { - setFocusedActivityLocked(r, "moveActivityToStack"); - } else { - mStackSupervisor.setFocusedStack(r, "moveActivityToStack"); - } - mStackSupervisor.resumeTopActivitiesLocked(r.task.stack, null, null); + mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, FORCE_FOCUS); } finally { Binder.restoreCallingIdentity(ident); } @@ -9048,7 +9048,7 @@ public final class ActivityManagerService extends ActivityManagerNative try { if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId + " to stackId=" + stackId + " toTop=" + toTop); - mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop); + mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop, !FORCE_FOCUS); } finally { Binder.restoreCallingIdentity(ident); } @@ -9074,9 +9074,9 @@ public final class ActivityManagerService extends ActivityManagerNative enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, "positionTaskInStack()"); if (stackId == HOME_STACK_ID) { - Slog.e(TAG, "positionTaskInStack: Attempt to change the position of task " - + taskId + " in/to home stack", - new RuntimeException("here").fillInStackTrace()); + throw new IllegalArgumentException( + "positionTaskInStack: Attempt to change the position of task " + + taskId + " in/to home stack"); } synchronized (this) { long ident = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 18a0a36d3794..356565fc802f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.DOCKED_STACK_ID; import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; @@ -229,6 +230,8 @@ final class ActivityStack { // Whether or not this stack covers the entire screen; by default stacks are fullscreen boolean mFullscreen = true; + // Current bounds of the stack or null if fullscreen. + Rect mBounds = null; long mLaunchStartTime = 0; long mFullyDrawnStartTime = 0; @@ -1227,8 +1230,42 @@ final class ActivityStack { return null; } - // Checks if any of the stacks above this one has a fullscreen activity behind it. - // If so, this stack is hidden, otherwise it is visible. + /** Returns true if the stack contains a fullscreen task. */ + private boolean hasFullscreenTask() { + for (int i = mTaskHistory.size() - 1; i >= 0; --i) { + final TaskRecord task = mTaskHistory.get(i); + if (task.mFullscreen) { + return true; + } + } + return false; + } + + /** Return true if this stack is hidden by the presence of a docked stack. */ + private boolean isHiddenByDockedStack() { + final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID); + if (dockedStack != null) { + final int dockedStackIndex = mStacks.indexOf(dockedStack); + final int stackIndex = mStacks.indexOf(this); + if (dockedStackIndex > stackIndex) { + // Fullscreen stacks or stacks with fullscreen task below the docked stack are not + // visible. We do this so we don't have the 2 stacks and their tasks overlap. + if (mFullscreen) { + return true; + } + + // We need to also check the tasks in the stack because they can be fullscreen + // even though their stack isn't due to their root activity not been resizeable + // (i.e. doesn't support multi-window mode). + if (hasFullscreenTask()) { + return true; + } + } + } + return false; + } + + /** Returns true if the stack is considered visible. */ private boolean isStackVisibleLocked() { if (!isAttached()) { return false; @@ -1238,12 +1275,15 @@ final class ActivityStack { return true; } - // Any stack that isn't the front stack is not visible, except for the case of the home - // stack behind the main application stack since we can have dialog activities on the - // main application stack that need the home stack to display behind them. - // TODO(multi-window): Also need to add exception for side-by-side stacks. - final boolean homeStack = mStackId == HOME_STACK_ID; - if (!homeStack) { + final int stackIndex = mStacks.indexOf(this); + + if (stackIndex == mStacks.size() - 1) { + Slog.wtf(TAG, + "Stack=" + this + " isn't front stack but is at the top of the stack list"); + return false; + } + + if (isHiddenByDockedStack()) { return false; } @@ -1252,13 +1292,24 @@ final class ActivityStack { * fullscreen activity, or a translucent activity that requested the * wallpaper to be shown behind it. */ - for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) { + for (int i = stackIndex + 1; i < mStacks.size(); i++) { final ActivityStack stack = mStacks.get(i); - if (stack.mStackId != FULLSCREEN_WORKSPACE_STACK_ID) { + final ArrayList<TaskRecord> tasks = stack.getAllTasks(); + + if (!stack.mFullscreen && !stack.hasFullscreenTask()) { + continue; + } + + if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID + || stack.mStackId == HOME_STACK_ID) { + // The freeform and home stacks can't have any other stack visible behind them + // when they are fullscreen since they act as base/cut-off points for visibility. + // NOTE: we don't cut-off at the FULLSCREEN_WORKSPACE_STACK_ID because the home + // stack sometimes needs to be visible behind it when it is displaying a dialog + // activity. We let it fall through to the logic below to determine visibility. return false; } - final ArrayList<TaskRecord> tasks = stack.getAllTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = tasks.get(taskNdx); // task above isn't fullscreen, so, we assume we're still visible. @@ -2680,9 +2731,10 @@ final class ActivityStack { ActivityRecord next = topRunningActivityLocked(null); final String myReason = reason + " adjustFocus"; if (next != r) { - if (next != null && mStackId == FREEFORM_WORKSPACE_STACK_ID) { - // For freeform stack we always keep the focus within the stack as long as - // there is a running activity in the stack that we can adjust focus to. + if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID + || mStackId == DOCKED_STACK_ID)) { + // For freeform and docked stacks we always keep the focus within the stack as + // long as there is a running activity in the stack that we can adjust focus to. mService.setFocusedActivityLocked(next, myReason); return; } else { @@ -4321,7 +4373,10 @@ final class ActivityStack { printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities, " ", "Hist", true, !dumpAll, dumpClient, dumpPackage, needSep, header, - " Task id #" + task.taskId); + " Task id #" + task.taskId + "\n" + + " mFullscreen=" + task.mFullscreen + "\n" + + " mBounds=" + task.mBounds + "\n" + + " mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds); if (printed) { header = null; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 464a1e4b45f4..957a403c9c89 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -184,6 +184,9 @@ public final class ActivityStackSupervisor implements DisplayListener { // (e.g. stack) is due to it moving to another container. static final boolean MOVING = true; + // Force the focus to change to the stack we are moving a task to.. + static final boolean FORCE_FOCUS = true; + // Activity actions an app cannot start if it uses a permission which is not granted. private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION = new ArrayMap<>(); @@ -328,6 +331,9 @@ public final class ActivityStackSupervisor implements DisplayListener { /** Used to keep resumeTopActivityLocked() from being entered recursively */ boolean inResumeTopActivity; + // temp. rect used during resize calculation so we don't need to create a new object each time. + private final Rect tempRect = new Rect(); + /** * Description of a request to start a new activity, which has been held * due to app switches being disabled. @@ -2908,16 +2914,13 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } - final ActivityRecord r = stack.topRunningActivityLocked(null); - if (r != null && !r.task.mResizeable) { - Slog.w(TAG, "resizeStack: top task " + r.task + " not resizeable."); - return; - } + ActivityRecord r = stack.topRunningActivityLocked(null); + final boolean resizeTasks = r != null && r.task.mResizeable; final IntArray changedTaskIds = new IntArray(stack.numTasks()); final List<Configuration> newTaskConfigs = new ArrayList<>(stack.numTasks()); - stack.mFullscreen = - mWindowManager.resizeStack(stackId, bounds, changedTaskIds, newTaskConfigs); + stack.mFullscreen = mWindowManager.resizeStack( + stackId, bounds, resizeTasks, changedTaskIds, newTaskConfigs); for (int i = changedTaskIds.size() - 1; i >= 0; i--) { final TaskRecord task = anyTaskForIdLocked(changedTaskIds.get(i), false); if (task == null) { @@ -2927,6 +2930,55 @@ public final class ActivityStackSupervisor implements DisplayListener { task.updateOverrideConfiguration(newTaskConfigs.get(i), bounds); } + if (stack.mStackId == DOCKED_STACK_ID) { + // Dock stack funness...Yay! + if (stack.mFullscreen) { + // The dock stack went fullscreen which is kinda like dismissing it. + // In this case we make all other static stacks fullscreen and move all + // docked stack tasks to the fullscreen stack. + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (i != DOCKED_STACK_ID) { + resizeStackLocked(i, null); + } + } + + final ArrayList<TaskRecord> tasks = stack.getAllTasks(); + final int count = tasks.size(); + for (int i = 0; i < count; i++) { + moveTaskToStackLocked(tasks.get(i).taskId, + FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS); + } + + // stack shouldn't contain anymore activities, so nothing to resume. + r = null; + } else { + // Docked stacks occupy a dedicated region on screen so the size of all other + // static stacks need to be adjusted so they don't overlap with the docked stack. + final int leftChange = stack.mBounds.left - bounds.left; + final int rightChange = stack.mBounds.right - bounds.right; + final int topChange = stack.mBounds.top - bounds.top; + final int bottomChange = stack.mBounds.bottom - bounds.bottom; + + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (i != DOCKED_STACK_ID) { + ActivityStack otherStack = getStack(i); + if (otherStack != null) { + tempRect.set(otherStack.mBounds); + // We adjust the opposing sides of the other stacks to + // the side in the dock stack that changed. + tempRect.left -= rightChange; + tempRect.right -= leftChange; + tempRect.top -= bottomChange; + tempRect.bottom -= topChange; + resizeStackLocked(i, tempRect); + } + } + } + + } + } + stack.mBounds = stack.mFullscreen ? null : new Rect(bounds); + if (r != null) { final boolean updated = stack.ensureActivityConfigurationLocked(r, 0); // And we need to make sure at this point that all other activities @@ -2968,7 +3020,8 @@ public final class ActivityStackSupervisor implements DisplayListener { final boolean wasFrontStack = isFrontStack(task.stack); if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) { stackId = FULLSCREEN_WORKSPACE_STACK_ID; - } else if (bounds != null && task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) { + } else if (bounds != null + && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) { stackId = FREEFORM_WORKSPACE_STACK_ID; } if (stackId != task.stack.mStackId) { @@ -3069,8 +3122,7 @@ public final class ActivityStackSupervisor implements DisplayListener { */ private ActivityStack moveTaskToStackUncheckedLocked( TaskRecord task, int stackId, boolean toTop, String reason) { - final ActivityStack stack = - getStack(stackId, CREATE_IF_NEEDED, toTop); + final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop); mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop); if (task.stack != null) { task.stack.removeTask(task, reason, MOVING); @@ -3079,26 +3131,39 @@ public final class ActivityStackSupervisor implements DisplayListener { return stack; } - void moveTaskToStackLocked(int taskId, int stackId, boolean toTop) { + void moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus) { final TaskRecord task = anyTaskForIdLocked(taskId); if (task == null) { Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId); return; } - ActivityStack stack = - moveTaskToStackUncheckedLocked(task, stackId, toTop, "moveTaskToStack"); + final String reason = "moveTaskToStack"; + final ActivityRecord top = task.topRunningActivityLocked(null); + final boolean adjustFocus = forceFocus || mService.mFocusedActivity == top; + final ActivityStack stack = + moveTaskToStackUncheckedLocked(task, stackId, toTop, reason); // Make sure the task has the appropriate bounds/size for the stack it is in. if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) { - resizeTaskLocked(task, null); + resizeTaskLocked(task, stack.mBounds); } else if (stackId == FREEFORM_WORKSPACE_STACK_ID && task.mBounds == null && task.mLastNonFullscreenBounds != null) { resizeTaskLocked(task, task.mLastNonFullscreenBounds); + } else if (stackId == DOCKED_STACK_ID) { + resizeTaskLocked(task, stack.mBounds); + } + + if (top != null && adjustFocus) { + if (mService.mFocusedActivity != top) { + mService.setFocusedActivityLocked(top, reason); + } else { + setFocusedStack(top, reason); + } } // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. - stack.ensureActivitiesVisibleLocked(null, 0); + ensureActivitiesVisibleLocked(null, 0); resumeTopActivitiesLocked(); } @@ -3659,6 +3724,10 @@ public final class ActivityStackSupervisor implements DisplayListener { stackHeader.append(" Stack #"); stackHeader.append(stack.mStackId); stackHeader.append(":"); + stackHeader.append("\n"); + stackHeader.append(" mFullscreen=" + stack.mFullscreen); + stackHeader.append("\n"); + stackHeader.append(" mBounds=" + stack.mBounds); printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, needSep, stackHeader.toString()); printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, @@ -4313,7 +4382,9 @@ public final class ActivityStackSupervisor implements DisplayListener { mStack.mStacks = activityDisplay.mStacks; activityDisplay.attachActivities(mStack, onTop); - mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop); + mStack.mBounds = + mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop); + mStack.mFullscreen = mStack.mBounds == null; } @Override diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index dd57f8075550..8fa0ae6a5ce9 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.DOCKED_STACK_ID; import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; @@ -1172,12 +1173,15 @@ final class TaskRecord { // is necessarily fullscreen. mFullscreen = Configuration.EMPTY.equals(mOverrideConfig); if (mFullscreen) { - if (mBounds != null) { + if (mBounds != null && stack.mStackId != DOCKED_STACK_ID) { mLastNonFullscreenBounds = mBounds; } mBounds = null; } else { - mBounds = mLastNonFullscreenBounds = new Rect(bounds); + mBounds = new Rect(bounds); + if (stack.mStackId != DOCKED_STACK_ID) { + mLastNonFullscreenBounds = mBounds; + } } return !mOverrideConfig.equals(oldConfig); } @@ -1207,6 +1211,8 @@ final class TaskRecord { || stack.mStackId == HOME_STACK_ID || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) { return null; + } else if (stack.mStackId == DOCKED_STACK_ID) { + return stack.mBounds; } return mLastNonFullscreenBounds; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2b26b42efe59..99e9fe63525f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -173,7 +173,7 @@ class DisplayContent { void updateDisplayInfo() { mDisplay.getDisplayInfo(mDisplayInfo); for (int i = mStacks.size() - 1; i >= 0; --i) { - mStacks.get(i).updateDisplayInfo(); + mStacks.get(i).updateDisplayInfo(null); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1eddb04aa951..554af2849919 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -41,6 +41,13 @@ class Task implements DimLayer.DimLayerUser { * when no window animation is driving it. */ private static final int DEFAULT_DIM_DURATION = 200; + // The amount we divide the height/width of the bounds we are trying to fit the task within + // when using the method {@link #fitWithinBounds}. + // We always want the task to to be visible in the bounds without affecting its size when + // fitting. To make sure this is the case, we don't adjust the task left or top side pass + // the input bounds right or bottom side minus the width or height divided by this value. + private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3; + TaskStack mStack; final AppTokenList mAppTokens = new AppTokenList(); final int mTaskId; @@ -165,6 +172,41 @@ class Task implements DimLayer.DimLayerUser { } } + /** Fits the tasks within the input bounds adjusting the task bounds as needed. + * @param bounds Bounds to fit the task within. Nothing is done if null. + * @return Returns true if the task bounds was adjusted in any way. + * */ + boolean fitWithinBounds(Rect bounds) { + if (bounds == null || bounds.contains(mBounds)) { + return false; + } + mTmpRect2.set(mBounds); + + if (mBounds.left < bounds.left || mBounds.right > bounds.right) { + final int maxRight = bounds.right - (bounds.width() / FIT_WITHIN_BOUNDS_DIVIDER); + int horizontalDiff = bounds.left - mBounds.left; + if ((horizontalDiff < 0 && mBounds.left >= maxRight) + || (mBounds.left + horizontalDiff >= maxRight)) { + horizontalDiff = maxRight - mBounds.left; + } + mTmpRect2.left += horizontalDiff; + mTmpRect2.right += horizontalDiff; + } + + if (mBounds.top < bounds.top || mBounds.bottom > bounds.bottom) { + final int maxBottom = bounds.bottom - (bounds.height() / FIT_WITHIN_BOUNDS_DIVIDER); + int verticalDiff = bounds.top - mBounds.top; + if ((verticalDiff < 0 && mBounds.top >= maxBottom) + || (mBounds.top + verticalDiff >= maxBottom)) { + verticalDiff = maxBottom - mBounds.top; + } + mTmpRect2.top += verticalDiff; + mTmpRect2.bottom += verticalDiff; + } + + return setBounds(mTmpRect2); + } + /** Set the task bounds. Passing in null sets the bounds to fullscreen. */ boolean setBounds(Rect bounds) { boolean oldFullscreen = mFullscreen; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 1c65c27e405f..25a71d9fc7f1 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -16,12 +16,14 @@ package com.android.server.wm; +import static android.app.ActivityManager.*; import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerService.TAG; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; +import android.os.RemoteException; import android.util.EventLog; import android.util.IntArray; import android.util.Slog; @@ -34,6 +36,10 @@ import java.util.ArrayList; import java.util.List; public class TaskStack implements DimLayer.DimLayerUser { + + // If the stack should be resized to fullscreen. + private static final boolean FULLSCREEN = true; + /** Unique identifier */ final int mStackId; @@ -91,20 +97,34 @@ public class TaskStack implements DimLayer.DimLayerUser { /** * Set the bounds of the stack and its containing tasks. * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen. + * @param resizeTasks If true, the tasks within the stack will also be resized. * @param changedTaskIds Output list of Ids of tasks that changed in bounds. * @param newTaskConfigs Output list of new Configuation of the tasks that changed. * @return True if the stack bounds was changed. * */ - boolean setBounds(Rect bounds, IntArray changedTaskIds, List<Configuration> newTaskConfigs) { + boolean setBounds(Rect bounds, boolean resizeTasks, IntArray changedTaskIds, + List<Configuration> newTaskConfigs) { if (!setBounds(bounds)) { return false; } + if (!resizeTasks) { + return true; + } + // Update bounds of containing tasks. final Rect newBounds = mFullscreen ? null : mBounds; for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task task = mTasks.get(taskNdx); - if (task.setBounds(newBounds)) { + if (mStackId == FREEFORM_WORKSPACE_STACK_ID) { + // For freeform stack we don't adjust the size of the tasks to match that of the + // stack, but we do try to make sure the tasks are still contained with the + // bounds of the stack. + if (task.fitWithinBounds(newBounds)) { + changedTaskIds.add(task.mTaskId); + newTaskConfigs.add(task.mOverrideConfig); + } + } else if (task.setBounds(newBounds)) { changedTaskIds.add(task.mTaskId); newTaskConfigs.add(task.mOverrideConfig); } @@ -146,9 +166,13 @@ public class TaskStack implements DimLayer.DimLayerUser { out.set(mBounds); } - void updateDisplayInfo() { + void updateDisplayInfo(Rect bounds) { if (mDisplayContent != null) { - setBounds(mFullscreen ? null : mBounds); + if (bounds != null) { + setBounds(bounds); + } else { + setBounds(mFullscreen ? null : mBounds); + } for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); } @@ -296,7 +320,82 @@ public class TaskStack implements DimLayer.DimLayerUser { mDisplayContent = displayContent; mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId()); - updateDisplayInfo(); + + Rect bounds = null; + final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null; + if (mStackId == DOCKED_STACK_ID || (dockedStackExists + && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) { + // The existence of a docked stack affects the size of any static stack created since + // the docked stack occupies a dedicated region on screen. + bounds = new Rect(); + displayContent.getLogicalDisplayRect(mTmpRect); + getInitialDockedStackBounds(mTmpRect, bounds, mStackId); + } + + updateDisplayInfo(bounds); + + if (mStackId == DOCKED_STACK_ID) { + // Attaching a docked stack to the display affects the size of all other static + // stacks since the docked stack occupies a dedicated region on screen. + // Resize existing static stacks so they are pushed to the side of the docked stack. + resizeNonDockedStacks(!FULLSCREEN); + } + } + + /** + * Outputs the initial bounds a stack should be given the presence of a docked stack on the + * display. + * @param displayRect The bounds of the display the docked stack is on. + * @param outBounds Output bounds that should be used for the stack. + * @param stackId Id of stack we are calculating the bounds for. + */ + private static void getInitialDockedStackBounds( + Rect displayRect, Rect outBounds, int stackId) { + // Docked stack start off occupying half the screen space. + // TODO(multi-window): Need to support the selecting which half of the screen the + // docked stack uses for snapping windows to the edge of the screen. + final boolean splitHorizontally = displayRect.width() > displayRect.height(); + outBounds.set(displayRect); + if (stackId == DOCKED_STACK_ID) { + if (splitHorizontally) { + outBounds.right = displayRect.centerX(); + } else { + outBounds.bottom = displayRect.centerY(); + } + } else { + if (splitHorizontally) { + outBounds.left = displayRect.centerX(); + } else { + outBounds.top = displayRect.centerY(); + } + } + } + + /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size + * based on the presence of a docked stack. + * @param fullscreen If true the stacks will be resized to fullscreen, else they will be + * resized to the appropriate size based on the presence of a docked stack. + */ + private void resizeNonDockedStacks(boolean fullscreen) { + mDisplayContent.getLogicalDisplayRect(mTmpRect); + if (!fullscreen) { + getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID); + } + + final int count = mService.mStackIdToStack.size(); + for (int i = 0; i < count; i++) { + final TaskStack otherStack = mService.mStackIdToStack.valueAt(i); + final int otherStackId = otherStack.mStackId; + if (otherStackId != DOCKED_STACK_ID + && otherStackId >= FIRST_STATIC_STACK_ID + && otherStackId <= LAST_STATIC_STACK_ID) { + try { + mService.mActivityManager.resizeStack(otherStackId, mTmpRect); + } catch (RemoteException e) { + // This will not happen since we are in the same process. + } + } + } } void detachDisplay() { @@ -317,6 +416,12 @@ public class TaskStack implements DimLayer.DimLayerUser { mService.requestTraversalLocked(); } + if (mStackId == DOCKED_STACK_ID) { + // Docked stack was detached from the display, so we no longer need to restrict the + // region of the screen other static stacks occupy. Go ahead and make them fullscreen. + resizeNonDockedStacks(FULLSCREEN); + } + close(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ec61a1dbcd84..355b09b555d7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5123,8 +5123,9 @@ public class WindowManagerService extends IWindowManager.Stub * @param displayId The unique identifier of the DisplayContent. * @param onTop If true the stack will be place at the top of the display, * else at the bottom + * @return The initial bounds the stack was created with. null means fullscreen. */ - public void attachStack(int stackId, int displayId, boolean onTop) { + public Rect attachStack(int stackId, int displayId, boolean onTop) { final long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { @@ -5138,16 +5139,24 @@ public class WindowManagerService extends IWindowManager.Stub } stack.attachDisplayContent(displayContent); displayContent.attachStack(stack, onTop); + moveStackWindowsLocked(displayContent); final WindowList windows = displayContent.getWindowList(); for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { windows.get(winNdx).reportResized(); } + if (stack.isFullscreen()) { + return null; + } + Rect bounds = new Rect(); + stack.getBounds(bounds); + return bounds; } } } finally { Binder.restoreCallingIdentity(origId); } + return null; } void detachStackLocked(DisplayContent displayContent, TaskStack stack) { @@ -5239,19 +5248,20 @@ public class WindowManagerService extends IWindowManager.Stub * Re-sizes a stack and its containing tasks. * @param stackId Id of stack to resize. * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen. + * @param resizeTasks If true, the tasks within the stack will also be resized. * @param changedTaskIds Output list of Ids of tasks that changed in bounds due to resize. * @param newTaskConfigs Output list of new Configuation of the tasks that changed. * @return True if the stack is now fullscreen. * */ - public boolean resizeStack( - int stackId, Rect bounds, IntArray changedTaskIds, List<Configuration> newTaskConfigs) { + public boolean resizeStack(int stackId, Rect bounds, boolean resizeTasks, + IntArray changedTaskIds, List<Configuration> newTaskConfigs) { synchronized (mWindowMap) { final TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { throw new IllegalArgumentException("resizeStack: stackId " + stackId + " not found."); } - if (stack.setBounds(bounds, changedTaskIds, newTaskConfigs)) { + if (stack.setBounds(bounds, resizeTasks, changedTaskIds, newTaskConfigs)) { stack.resizeWindows(); stack.getDisplayContent().layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); |