summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java15
-rw-r--r--services/core/java/com/android/server/am/LaunchingTaskPositioner.java142
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java8
4 files changed, 190 insertions, 15 deletions
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 356565fc802f..fcd596f28c5a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -248,6 +248,8 @@ final class ActivityStack {
/** Run all ActivityStacks through this */
final ActivityStackSupervisor mStackSupervisor;
+ private final LaunchingTaskPositioner mTaskPositioner;
+
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -363,6 +365,33 @@ final class ActivityStack {
mStackId = activityContainer.mStackId;
mCurrentUser = mService.mCurrentUserId;
mRecentTasks = recentTasks;
+ mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
+ ? new LaunchingTaskPositioner() : null;
+ }
+
+ void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
+ mDisplayId = activityDisplay.mDisplayId;
+ mStacks = activityDisplay.mStacks;
+ mBounds = mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
+ mFullscreen = mBounds == null;
+ if (mTaskPositioner != null) {
+ mTaskPositioner.setDisplay(activityDisplay.mDisplay);
+ mTaskPositioner.configure(mBounds);
+ }
+ }
+
+ void detachDisplay() {
+ mDisplayId = Display.INVALID_DISPLAY;
+ mStacks = null;
+ if (mTaskPositioner != null) {
+ mTaskPositioner.reset();
+ }
+ mWindowManager.detachStack(mStackId);
+ }
+
+ void setBounds(Rect bounds) {
+ mBounds = mFullscreen ? null : new Rect(bounds);
+ mTaskPositioner.configure(bounds);
}
boolean okToShowLocked(ActivityRecord r) {
@@ -2223,7 +2252,7 @@ final class ActivityStack {
+ task, new RuntimeException("here").fillInStackTrace());
task.addActivityToTop(r);
r.putInHistory();
- addAppToken(r, task);
+ addConfigOverride(r, task);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
@@ -2283,7 +2312,7 @@ final class ActivityStack {
: AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
mNoAnimActivities.remove(r);
}
- addAppToken(r, task);
+ addConfigOverride(r, task);
boolean doShow = true;
if (newTask) {
// Even though this activity is starting fresh, we still need
@@ -2332,7 +2361,7 @@ final class ActivityStack {
} else {
// If this is the first activity, don't do any fancy animations,
// because there is nothing for it to animate on top of.
- addAppToken(r, task);
+ addConfigOverride(r, task);
ActivityOptions.abort(options);
options = null;
}
@@ -4513,6 +4542,9 @@ final class ActivityStack {
boolean toTop) {
TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
voiceInteractor);
+ if (mTaskPositioner != null) {
+ mTaskPositioner.updateDefaultBounds(task, mTaskHistory);
+ }
addTask(task, toTop, false);
return task;
}
@@ -4548,7 +4580,7 @@ final class ActivityStack {
}
}
- void addAppToken(ActivityRecord r, TaskRecord task) {
+ void addConfigOverride(ActivityRecord r, TaskRecord task) {
final Rect bounds = task.getLaunchBounds();
final Configuration config =
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 12b848be6ea3..0bdeed771ba8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2977,7 +2977,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
- stack.mBounds = stack.mFullscreen ? null : new Rect(bounds);
+ stack.setBounds(bounds);
if (r != null) {
final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
@@ -3112,7 +3112,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- stack.addAppToken(activities.get(activityNdx), task);
+ stack.addConfigOverride(activities.get(activityNdx), task);
}
return true;
}
@@ -4387,13 +4387,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (DEBUG_STACK) Slog.d(TAG_STACK, "attachToDisplayLocked: " + this
+ " to display=" + activityDisplay + " onTop=" + onTop);
mActivityDisplay = activityDisplay;
- mStack.mDisplayId = activityDisplay.mDisplayId;
- mStack.mStacks = activityDisplay.mStacks;
-
+ mStack.attachDisplay(activityDisplay, onTop);
activityDisplay.attachActivities(mStack, onTop);
- mStack.mBounds =
- mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
- mStack.mFullscreen = mStack.mBounds == null;
}
@Override
@@ -4465,9 +4460,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (mActivityDisplay != null) {
mActivityDisplay.detachActivitiesLocked(mStack);
mActivityDisplay = null;
- mStack.mDisplayId = -1;
- mStack.mStacks = null;
- mWindowManager.detachStack(mStackId);
+ mStack.detachDisplay();
}
}
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
new file mode 100644
index 000000000000..3005c869ddcf
--- /dev/null
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+
+import java.util.ArrayList;
+
+/**
+ * Determines where a launching task should be positioned and sized on the display.
+ */
+class LaunchingTaskPositioner {
+ // Determines how close window frames/corners have to be to call them colliding.
+ private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4;
+
+ // Task will receive dimensions based on available dimensions divided by this.
+ private static final int WINDOW_SIZE_DENOMINATOR = 2;
+
+ // Task will receive margins based on available dimensions divided by this.
+ private static final int MARGIN_SIZE_DENOMINATOR = 4;
+
+ // If task bounds collide with some other, we will step and try again until we find a good
+ // position. The step will be determined by using dimensions and dividing it by this.
+ private static final int STEP_DENOMINATOR = 16;
+
+ // We always want to step by at least this.
+ private static final int MINIMAL_STEP = 1;
+
+ private boolean mDefaultStartBoundsConfigurationSet = false;
+ private final Rect mAvailableRect = new Rect();
+ private int mDefaultFreeformStartX;
+ private int mDefaultFreeformStartY;
+ private int mDefaultFreeformWidth;
+ private int mDefaultFreeformHeight;
+ private int mDefaultFreeformStepHorizontal;
+ private int mDefaultFreeformStepVertical;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
+
+ void setDisplay(Display display) {
+ Point size = new Point();
+ display.getSize(size);
+ mDisplayWidth = size.x;
+ mDisplayHeight = size.y;
+ }
+
+ void configure(Rect stackBounds) {
+ if (stackBounds == null) {
+ mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ } else {
+ mAvailableRect.set(stackBounds);
+ }
+ int width = mAvailableRect.width();
+ int height = mAvailableRect.height();
+ mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR;
+ mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR;
+ mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR;
+ mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR;
+ mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP);
+ mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP);
+ mDefaultStartBoundsConfigurationSet = true;
+ }
+
+ /**
+ * Tries to set task's bound in a way that it won't collide with any other task. By colliding
+ * we mean that two tasks have left-top corner very close to each other, so one might get
+ * obfuscated by the other one.
+ *
+ * @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.
+ */
+ void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks) {
+ if (!mDefaultStartBoundsConfigurationSet) {
+ return;
+ }
+ int startX = mDefaultFreeformStartX;
+ int startY = mDefaultFreeformStartY;
+ final int right = mAvailableRect.right;
+ final int bottom = mAvailableRect.bottom;
+ boolean restarted = false;
+ while (boundsConflict(startX, startY, 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;
+ restarted = true;
+ }
+ if (restarted
+ && (startX > mDefaultFreeformStartX || startY > 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;
+ break;
+ }
+ }
+ task.setInitialBounds(startX, startY, startX + mDefaultFreeformWidth,
+ startY + mDefaultFreeformHeight);
+ }
+
+ private boolean boundsConflict(int startX, int startY, ArrayList<TaskRecord> tasks) {
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ TaskRecord task = tasks.get(i);
+ if (!task.mActivities.isEmpty()) {
+ 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)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ 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 ed935a139f45..ff412d1167b9 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1217,6 +1217,14 @@ final class TaskRecord {
return mLastNonFullscreenBounds;
}
+ void setInitialBounds(int left, int top, int right, int bottom) {
+ if (mBounds == null) {
+ mBounds = new Rect();
+ }
+ mBounds.set(left, top, right, bottom);
+ mLastNonFullscreenBounds = mBounds;
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("userId="); pw.print(userId);
pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);