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/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();