Added support for docked stack
Docked stacks occupy a dedicated region on a display.
When a docked stack is present all other static stacks bounds
are restricted to the region of the screen not occupied by
the docked stack.
Change-Id: I6aec3aa19c41a7e756375002f3a925977b5533b5
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d54930f..f3ea1c4 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 @@
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.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 @@
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 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 @@
}
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 @@
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 @@
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 18a0a36..356565f 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 @@
// 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 @@
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 @@
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 @@
* 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 @@
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 @@
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 a449baf..5a338af 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 @@
// (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 @@
/** 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 @@
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 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 dd57f80..8fa0ae6 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 @@
// 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 @@
|| 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 2b26b42..99e9fe6 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 @@
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 1eddb04..554af28 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 @@
* 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 @@
}
}
+ /** 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 1c65c27..25a71d9 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.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 @@
/**
* 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 @@
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 @@
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 @@
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 ec61a1d..355b09b 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 @@
* @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 @@
}
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 @@
* 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();