summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--core/java/android/content/pm/ActivityInfo.java1
-rw-r--r--core/java/android/content/pm/PackageParser.java13
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java2
-rw-r--r--services/core/java/com/android/server/am/LaunchingTaskPositioner.java211
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java4
9 files changed, 205 insertions, 36 deletions
diff --git a/api/current.txt b/api/current.txt
index b1e80dd2a672..4157c6ffb3cf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -240,8 +240,10 @@ package android {
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
+ field public static final int activityHeight = 16844019; // 0x10104f3
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
+ field public static final int activityWidth = 16844018; // 0x10104f2
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
diff --git a/api/system-current.txt b/api/system-current.txt
index fe7c6dac610e..33ce543c9ff8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -333,8 +333,10 @@ package android {
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
+ field public static final int activityHeight = 16844019; // 0x10104f3
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
+ field public static final int activityWidth = 16844018; // 0x10104f2
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 876fbf55765c..83092a9ab034 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -719,6 +719,7 @@ public class ActivityInfo extends ComponentInfo
maxRecents = orig.maxRecents;
resizeable = orig.resizeable;
lockTaskLaunchMode = orig.lockTaskLaunchMode;
+ initialLayout = orig.initialLayout;
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6443667ab1db..502f73562418 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3307,25 +3307,25 @@ public class PackageParser {
int height = -1;
float heightFraction = -1f;
final int widthType = sw.getType(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_width);
+ com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth);
if (widthType == TypedValue.TYPE_FRACTION) {
widthFraction = sw.getFraction(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_width,
+ com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth,
1, 1, -1);
} else if (widthType == TypedValue.TYPE_DIMENSION) {
width = sw.getDimensionPixelSize(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_width,
+ com.android.internal.R.styleable.AndroidManifestInitialLayout_activityWidth,
-1);
}
final int heightType = sw.getType(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_height);
+ com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight);
if (heightType == TypedValue.TYPE_FRACTION) {
heightFraction = sw.getFraction(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_height,
+ com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight,
1, 1, -1);
} else if (heightType == TypedValue.TYPE_DIMENSION) {
height = sw.getDimensionPixelSize(
- com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_height,
+ com.android.internal.R.styleable.AndroidManifestInitialLayout_activityHeight,
-1);
}
int gravity = sw.getInt(
@@ -3415,6 +3415,7 @@ public class PackageParser {
info.uiOptions = target.info.uiOptions;
info.parentActivityName = target.info.parentActivityName;
info.maxRecents = target.info.maxRecents;
+ info.initialLayout = target.info.initialLayout;
Activity a = new Activity(mParseActivityAliasArgs, info);
if (outError[0] != null) {
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 74fd1eccd4db..1a2600d47820 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2200,10 +2200,10 @@
<declare-styleable name="AndroidManifestInitialLayout" parent="AndroidManifestActivity">
<!-- Initial width of the activity. Can be either a fixed value or fraction, in which case
the width will be constructed as a fraction of the total available width. -->
- <attr name="activity_width" format="dimension|fraction" />
+ <attr name="activityWidth" format="dimension|fraction" />
<!-- Initial height of the activity. Can be either a fixed value or fraction, in which case
the height will be constructed as a fraction of the total available height. -->
- <attr name="activity_height" format="dimension|fraction" />
+ <attr name="activityHeight" format="dimension|fraction" />
<!-- Where to initially position the activity inside the available space. Uses constants
defined in {@link android.view.Gravity}. -->
<attr name="gravity" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cabb56cb006d..d2089cdad73b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2679,5 +2679,7 @@
<public type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
<public type="id" name="accessibilityActionSetProgress" />
+ <public type="attr" name="activityWidth" />
+ <public type="attr" name="activityHeight" />
</resources>
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index fcd596f28c5a..965f5b626508 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4543,7 +4543,7 @@ final class ActivityStack {
TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
voiceInteractor);
if (mTaskPositioner != null) {
- mTaskPositioner.updateDefaultBounds(task, mTaskHistory);
+ mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.initialLayout);
}
addTask(task, toTop, false);
return task;
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index 3005c869ddcf..5c4fd138c19c 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -16,16 +16,30 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
+import android.util.Slog;
import android.view.Display;
+import android.view.Gravity;
import java.util.ArrayList;
/**
* Determines where a launching task should be positioned and sized on the display.
+ *
+ * The positioner is fairly simple. For the new task it tries default position based on the gravity
+ * and compares corners of the task with corners of existing tasks. If some two pairs of corners are
+ * sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts
+ * all possible shifts, it gives up and puts the task in the original position.
*/
class LaunchingTaskPositioner {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchingTaskPositioner" : TAG_AM;
+
// Determines how close window frames/corners have to be to call them colliding.
private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4;
@@ -42,8 +56,19 @@ class LaunchingTaskPositioner {
// We always want to step by at least this.
private static final int MINIMAL_STEP = 1;
+ // Used to indicate if positioning algorithm is allowed to restart from the beginning, when it
+ // reaches the end of stack bounds.
+ private static final boolean ALLOW_RESTART = true;
+
+ private static final int SHIFT_POLICY_DIAGONAL_DOWN = 1;
+ private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2;
+ private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3;
+
private boolean mDefaultStartBoundsConfigurationSet = false;
private final Rect mAvailableRect = new Rect();
+ private final Rect mTmpProposal = new Rect();
+ private final Rect mTmpOriginal = new Rect();
+
private int mDefaultFreeformStartX;
private int mDefaultFreeformStartY;
private int mDefaultFreeformWidth;
@@ -84,51 +109,167 @@ class LaunchingTaskPositioner {
*
* @param task Task for which we want to find bounds that won't collide with other.
* @param tasks Existing tasks with which we don't want to collide.
+ * @param initialLayout Optional information from the client about how it would like to be sized
+ * and positioned.
*/
- void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks) {
+ void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
+ @Nullable ActivityInfo.InitialLayout initialLayout) {
if (!mDefaultStartBoundsConfigurationSet) {
return;
}
- int startX = mDefaultFreeformStartX;
- int startY = mDefaultFreeformStartY;
- final int right = mAvailableRect.right;
- final int bottom = mAvailableRect.bottom;
+ if (initialLayout == null) {
+ positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight);
+ return;
+ }
+ int width = getFinalWidth(initialLayout);
+ int height = getFinalHeight(initialLayout);
+ int verticalGravity = initialLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ int horizontalGravity = initialLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ if (verticalGravity == Gravity.TOP) {
+ if (horizontalGravity == Gravity.RIGHT) {
+ positionTopRight(task, tasks, width, height);
+ } else {
+ positionTopLeft(task, tasks, width, height);
+ }
+ } else if (verticalGravity == Gravity.BOTTOM) {
+ if (horizontalGravity == Gravity.RIGHT) {
+ positionBottomRight(task, tasks, width, height);
+ } else {
+ positionBottomLeft(task, tasks, width, height);
+ }
+ } else {
+ // Some fancy gravity setting that we don't support yet. We just put the activity in the
+ // center.
+ Slog.w(TAG, "Received unsupported gravity: " + initialLayout.gravity
+ + ", positioning in the center instead.");
+ positionCenter(task, tasks, width, height);
+ }
+ }
+
+ private int getFinalWidth(ActivityInfo.InitialLayout initialLayout) {
+ int width = mDefaultFreeformWidth;
+ if (initialLayout.width > 0) {
+ width = initialLayout.width;
+ }
+ if (initialLayout.widthFraction > 0) {
+ width = (int) (mAvailableRect.width() * initialLayout.widthFraction);
+ }
+ return width;
+ }
+
+ private int getFinalHeight(ActivityInfo.InitialLayout initialLayout) {
+ int height = mDefaultFreeformHeight;
+ if (initialLayout.height > 0) {
+ height = initialLayout.height;
+ }
+ if (initialLayout.heightFraction > 0) {
+ height = (int) (mAvailableRect.height() * initialLayout.heightFraction);
+ }
+ return height;
+ }
+
+ private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.left, mAvailableRect.bottom - height,
+ mAvailableRect.left + width, mAvailableRect.bottom);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT);
+ }
+
+ private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.bottom - height,
+ mAvailableRect.right, mAvailableRect.bottom);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT);
+ }
+
+ private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.left, mAvailableRect.top,
+ mAvailableRect.left + width, mAvailableRect.top + height);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT);
+ }
+
+ private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.top,
+ mAvailableRect.right, mAvailableRect.top + height);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT);
+ }
+
+ private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mDefaultFreeformStartX, mDefaultFreeformStartY,
+ mDefaultFreeformStartX + width, mDefaultFreeformStartY + height);
+ position(task, tasks, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN);
+ }
+
+ private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect proposal,
+ boolean allowRestart, int shiftPolicy) {
+ mTmpOriginal.set(proposal);
boolean restarted = false;
- while (boundsConflict(startX, startY, tasks)) {
+ while (boundsConflict(proposal, tasks)) {
// Unfortunately there is already a task at that spot, so we need to look for some
// other place.
- startX += mDefaultFreeformStepHorizontal;
- startY += mDefaultFreeformStepVertical;
- if (startX + mDefaultFreeformWidth > right
- || startY + mDefaultFreeformHeight > bottom) {
- // We don't want the task to go outside of the display, because it won't look
- // nice. Let's restart from the top instead, because there should be some space
- // there.
- startX = mAvailableRect.left;
- startY = mAvailableRect.top;
+ shiftStartingPoint(proposal, shiftPolicy);
+ if (shiftedToFar(proposal, shiftPolicy)) {
+ // We don't want the task to go outside of the stack, because it won't look
+ // nice. Depending on the starting point we either restart, or immediately give up.
+ if (!allowRestart) {
+ proposal.set(mTmpOriginal);
+ break;
+ }
+ // We must have started not from the top. Let's restart from there because there
+ // might be some space there.
+ proposal.set(mAvailableRect.left, mAvailableRect.top,
+ mAvailableRect.left + proposal.width(),
+ mAvailableRect.top + proposal.height());
restarted = true;
}
- if (restarted
- && (startX > mDefaultFreeformStartX || startY > mDefaultFreeformStartY)) {
+ if (restarted && (proposal.left > mDefaultFreeformStartX
+ || proposal.top > mDefaultFreeformStartY)) {
// If we restarted and crossed the initial position, let's not struggle anymore.
// The user already must have ton of tasks visible, we can just smack the new
// one in the center.
- startX = mDefaultFreeformStartX;
- startY = mDefaultFreeformStartY;
+ proposal.set(mTmpOriginal);
break;
}
}
- task.setInitialBounds(startX, startY, startX + mDefaultFreeformWidth,
- startY + mDefaultFreeformHeight);
+ task.setInitialBounds(proposal);
+ }
+
+ private boolean shiftedToFar(Rect start, int shiftPolicy) {
+ switch (shiftPolicy) {
+ case SHIFT_POLICY_HORIZONTAL_LEFT:
+ return start.left < mAvailableRect.left;
+ case SHIFT_POLICY_HORIZONTAL_RIGHT:
+ return start.right > mAvailableRect.right;
+ default: // SHIFT_POLICY_DIAGONAL_DOWN
+ return start.right > mAvailableRect.right || start.bottom > mAvailableRect.bottom;
+ }
}
- private boolean boundsConflict(int startX, int startY, ArrayList<TaskRecord> tasks) {
+ private void shiftStartingPoint(Rect posposal, int shiftPolicy) {
+ switch (shiftPolicy) {
+ case SHIFT_POLICY_HORIZONTAL_LEFT:
+ posposal.offset(-mDefaultFreeformStepHorizontal, 0);
+ break;
+ case SHIFT_POLICY_HORIZONTAL_RIGHT:
+ posposal.offset(mDefaultFreeformStepHorizontal, 0);
+ break;
+ default: // SHIFT_POLICY_DIAGONAL_DOWN:
+ posposal.offset(mDefaultFreeformStepHorizontal, mDefaultFreeformStepVertical);
+ break;
+ }
+ }
+
+ private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) {
for (int i = tasks.size() - 1; i >= 0; i--) {
TaskRecord task = tasks.get(i);
- if (!task.mActivities.isEmpty()) {
+ if (!task.mActivities.isEmpty() && task.mBounds != null) {
Rect bounds = task.mBounds;
- if (bounds != null && (Math.abs(bounds.left - startX) < BOUNDS_CONFLICT_MIN_DISTANCE
- || Math.abs(bounds.top - startY) < BOUNDS_CONFLICT_MIN_DISTANCE)) {
+ if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds)
+ || closeLeftBottomCorner(proposal, bounds)
+ || closeRightBottomCorner(proposal, bounds)) {
return true;
}
}
@@ -136,6 +277,26 @@ class LaunchingTaskPositioner {
return false;
}
+ private static final boolean closeLeftTopCorner(Rect first, Rect second) {
+ return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE
+ && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE;
+ }
+
+ private static final boolean closeRightTopCorner(Rect first, Rect second) {
+ return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE
+ && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE;
+ }
+
+ private static final boolean closeLeftBottomCorner(Rect first, Rect second) {
+ return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE
+ && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE;
+ }
+
+ private static final boolean closeRightBottomCorner(Rect first, Rect second) {
+ return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE
+ && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE;
+ }
+
void reset() {
mDefaultStartBoundsConfigurationSet = false;
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index ff412d1167b9..712b8cc9af6b 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1217,11 +1217,11 @@ final class TaskRecord {
return mLastNonFullscreenBounds;
}
- void setInitialBounds(int left, int top, int right, int bottom) {
+ void setInitialBounds(Rect rect) {
if (mBounds == null) {
mBounds = new Rect();
}
- mBounds.set(left, top, right, bottom);
+ mBounds.set(rect);
mLastNonFullscreenBounds = mBounds;
}