diff options
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); |