diff options
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | api/system-current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/content/pm/ActivityInfo.java | 1 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 13 | ||||
| -rw-r--r-- | core/res/res/values/attrs_manifest.xml | 4 | ||||
| -rw-r--r-- | core/res/res/values/public.xml | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityStack.java | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/LaunchingTaskPositioner.java | 211 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/TaskRecord.java | 4 |
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; } |