Merge "Using initial activity layout to position the launching activity."

This commit is contained in:
Filip Gruszczynski
2015-08-24 14:39:03 +00:00
committed by Android (Google) Code Review
9 changed files with 205 additions and 36 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -719,6 +719,7 @@ public class ActivityInfo extends ComponentInfo
maxRecents = orig.maxRecents;
resizeable = orig.resizeable;
lockTaskLaunchMode = orig.lockTaskLaunchMode;
initialLayout = orig.initialLayout;
}
/**

View File

@@ -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) {

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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;

View File

@@ -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 boundsConflict(int startX, int startY, ArrayList<TaskRecord> tasks) {
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 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;
}

View File

@@ -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;
}