Initial implementation of LaunchingBoundsController.
This changelist introduces the LaunchingBoundsController, a central location for calculating launch bounds. It also defines a positioner interface that can be registered with LaunchingBoundsController to participate in bounds calculation. Test: bit FrameworksServicesTests:com.android.server.am.LaunchingTaskPositionerTests Test: bit FrameworksServicesTests:com.android.server.am.LaunchingActivityPositionerTests Test: bit FrameworksServicesTests:com.android.server.am.LaunchingBoundsControllerTests Bug: 64144308 Change-Id: I35eaf095e2edd562375403413050ce82618c44f2
This commit is contained in:
@@ -107,7 +107,6 @@ import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
@@ -346,7 +345,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
|
||||
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
|
||||
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
|
||||
private final Rect mTmpRect2 = new Rect();
|
||||
private final Point mTmpSize = new Point();
|
||||
|
||||
/** Run all ActivityStacks through this */
|
||||
protected final ActivityStackSupervisor mStackSupervisor;
|
||||
@@ -5123,24 +5121,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
|
||||
addTask(task, toTop, "createTaskRecord");
|
||||
final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController
|
||||
.isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
|
||||
if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
|
||||
&& !isLockscreenShown) {
|
||||
if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout)
|
||||
&& mBounds != null && task.isResizeable() && !isLockscreenShown) {
|
||||
task.updateOverrideConfiguration(mBounds);
|
||||
}
|
||||
task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
|
||||
return task;
|
||||
}
|
||||
|
||||
boolean layoutTaskInStack(TaskRecord task, ActivityInfo.WindowLayout windowLayout) {
|
||||
if (!task.inFreeformWindowingMode()) {
|
||||
return false;
|
||||
}
|
||||
mStackSupervisor.getLaunchingTaskPositioner()
|
||||
.updateDefaultBounds(task, mTaskHistory, windowLayout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrayList<TaskRecord> getAllTasks() {
|
||||
return new ArrayList<>(mTaskHistory);
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
WindowManagerService mWindowManager;
|
||||
DisplayManager mDisplayManager;
|
||||
|
||||
LaunchingTaskPositioner mTaskPositioner = new LaunchingTaskPositioner();
|
||||
private final LaunchingBoundsController mLaunchingBoundsController;
|
||||
|
||||
/** Counter for next free stack ID to use for dynamic activity stacks. */
|
||||
private int mNextFreeStackId = 0;
|
||||
@@ -575,6 +575,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
mHandler = new ActivityStackSupervisorHandler(looper);
|
||||
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
|
||||
mKeyguardController = new KeyguardController(service, this);
|
||||
|
||||
mLaunchingBoundsController = new LaunchingBoundsController();
|
||||
mLaunchingBoundsController.registerDefaultPositioners(this);
|
||||
}
|
||||
|
||||
void setRecentTasks(RecentTasks recentTasks) {
|
||||
@@ -2161,8 +2164,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
|| mService.mSupportsFreeformWindowManagement;
|
||||
}
|
||||
|
||||
LaunchingTaskPositioner getLaunchingTaskPositioner() {
|
||||
return mTaskPositioner;
|
||||
LaunchingBoundsController getLaunchingBoundsController() {
|
||||
return mLaunchingBoundsController;
|
||||
}
|
||||
|
||||
protected <T extends ActivityStack> T getStack(int stackId) {
|
||||
|
||||
@@ -151,7 +151,7 @@ class ActivityStarter {
|
||||
private boolean mLaunchTaskBehind;
|
||||
private int mLaunchFlags;
|
||||
|
||||
private Rect mLaunchBounds;
|
||||
private Rect mLaunchBounds = new Rect();
|
||||
|
||||
private ActivityRecord mNotTop;
|
||||
private boolean mDoResume;
|
||||
@@ -210,7 +210,7 @@ class ActivityStarter {
|
||||
mLaunchFlags = 0;
|
||||
mLaunchMode = INVALID_LAUNCH_MODE;
|
||||
|
||||
mLaunchBounds = null;
|
||||
mLaunchBounds.setEmpty();
|
||||
|
||||
mNotTop = null;
|
||||
mDoResume = false;
|
||||
@@ -1254,7 +1254,10 @@ class ActivityStarter {
|
||||
|
||||
mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);
|
||||
|
||||
mLaunchBounds = getOverrideBounds(r, options, inTask);
|
||||
mLaunchBounds.setEmpty();
|
||||
|
||||
mSupervisor.getLaunchingBoundsController().calculateBounds(inTask, null /*layout*/, r,
|
||||
options, mLaunchBounds);
|
||||
|
||||
mLaunchMode = r.launchMode;
|
||||
|
||||
@@ -1725,7 +1728,7 @@ class ActivityStarter {
|
||||
// Target stack got cleared when we all activities were removed above.
|
||||
// Go ahead and reset it.
|
||||
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
|
||||
null /* bounds */, mLaunchFlags, mOptions);
|
||||
mLaunchFlags, mOptions);
|
||||
mTargetStack.addTask(task,
|
||||
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
|
||||
}
|
||||
@@ -1776,8 +1779,7 @@ class ActivityStarter {
|
||||
|
||||
private int setTaskFromReuseOrCreateNewTask(
|
||||
TaskRecord taskToAffiliate, ActivityStack topStack) {
|
||||
mTargetStack = computeStackFocus(
|
||||
mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);
|
||||
mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
|
||||
|
||||
// Do no move the target stack to front yet, as we might bail if
|
||||
// isLockTaskModeViolation fails below.
|
||||
@@ -1962,7 +1964,7 @@ class ActivityStarter {
|
||||
return START_TASK_TO_FRONT;
|
||||
}
|
||||
|
||||
if (mLaunchBounds != null) {
|
||||
if (!mLaunchBounds.isEmpty()) {
|
||||
// TODO: Shouldn't we already know what stack to use by the time we get here?
|
||||
ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
|
||||
if (stack != mInTask.getStack()) {
|
||||
@@ -1985,7 +1987,7 @@ class ActivityStarter {
|
||||
}
|
||||
|
||||
void updateBounds(TaskRecord task, Rect bounds) {
|
||||
if (bounds == null) {
|
||||
if (bounds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1998,8 +2000,7 @@ class ActivityStarter {
|
||||
}
|
||||
|
||||
private void setTaskToCurrentTopOrCreateNewTask() {
|
||||
mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
|
||||
mOptions);
|
||||
mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
|
||||
if (mDoResume) {
|
||||
mTargetStack.moveToFront("addingToTopTask");
|
||||
}
|
||||
@@ -2062,8 +2063,8 @@ class ActivityStarter {
|
||||
}
|
||||
}
|
||||
|
||||
private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
|
||||
int launchFlags, ActivityOptions aOptions) {
|
||||
private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
|
||||
ActivityOptions aOptions) {
|
||||
final TaskRecord task = r.getTask();
|
||||
ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
|
||||
if (stack != null) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.app.ActivityOptions;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Rect;
|
||||
import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LaunchingBoundsPositioner}, which applies the launch bounds specified
|
||||
* inside {@link ActivityOptions#getLaunchBounds()}.
|
||||
*/
|
||||
public class LaunchingActivityPositioner implements LaunchingBoundsPositioner {
|
||||
private final ActivityStackSupervisor mSupervisor;
|
||||
|
||||
LaunchingActivityPositioner(ActivityStackSupervisor activityStackSupervisor) {
|
||||
mSupervisor = activityStackSupervisor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
|
||||
ActivityRecord activity, ActivityOptions options, Rect current, Rect result) {
|
||||
// We only care about figuring out bounds for activities.
|
||||
if (activity == null) {
|
||||
return RESULT_SKIP;
|
||||
}
|
||||
|
||||
// Activity must be resizeable in the specified task.
|
||||
if (!(mSupervisor.canUseActivityOptionsLaunchBounds(options)
|
||||
&& (activity.isResizeable() || (task != null && task.isResizeable())))) {
|
||||
return RESULT_SKIP;
|
||||
}
|
||||
|
||||
final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
|
||||
|
||||
// Bounds weren't valid.
|
||||
if (bounds == null) {
|
||||
return RESULT_SKIP;
|
||||
}
|
||||
|
||||
result.set(bounds);
|
||||
|
||||
// When this is the most explicit position specification so we should not allow further
|
||||
// modification of the position.
|
||||
return RESULT_DONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.annotation.IntDef;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.pm.ActivityInfo.WindowLayout;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
|
||||
|
||||
/**
|
||||
* {@link LaunchingBoundsController} calculates the launch bounds by coordinating between registered
|
||||
* {@link LaunchingBoundsPositioner}.
|
||||
*/
|
||||
class LaunchingBoundsController {
|
||||
private final List<LaunchingBoundsPositioner> mPositioners = new ArrayList<>();
|
||||
|
||||
// Temporary {@link Rect} for calculations. This is kept separate from {@code mTmpCurrent} and
|
||||
// {@code mTmpResult} to prevent clobbering values.
|
||||
private final Rect mTmpRect = new Rect();
|
||||
|
||||
private final Rect mTmpCurrent = new Rect();
|
||||
private final Rect mTmpResult = new Rect();
|
||||
|
||||
/**
|
||||
* Creates a {@link LaunchingBoundsController} with default registered
|
||||
* {@link LaunchingBoundsPositioner}s.
|
||||
*/
|
||||
void registerDefaultPositioners(ActivityStackSupervisor supervisor) {
|
||||
// {@link LaunchingTaskPositioner} handles window layout preferences.
|
||||
registerPositioner(new LaunchingTaskPositioner());
|
||||
|
||||
// {@link LaunchingActivityPositioner} is the most specific positioner and thus should be
|
||||
// registered last (applied first) out of the defaults.
|
||||
registerPositioner(new LaunchingActivityPositioner(supervisor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position calculated by the registered positioners
|
||||
* @param task The {@link TaskRecord} currently being positioned.
|
||||
* @param layout The specified {@link WindowLayout}.
|
||||
* @param activity The {@link ActivityRecord} currently being positioned.
|
||||
* @param options The {@link ActivityOptions} specified for the activity.
|
||||
* @param result The resulting bounds. If no bounds are set, {@link Rect#isEmpty()} will be
|
||||
* true.
|
||||
*/
|
||||
void calculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
|
||||
ActivityOptions options, Rect result) {
|
||||
result.setEmpty();
|
||||
|
||||
// We start at the last registered {@link LaunchingBoundsPositioner} as this represents
|
||||
// The positioner closest to the product level. Moving back through the list moves closer to
|
||||
// the platform logic.
|
||||
for (int i = mPositioners.size() - 1; i >= 0; --i) {
|
||||
mTmpResult.setEmpty();
|
||||
mTmpCurrent.set(result);
|
||||
final LaunchingBoundsPositioner positioner = mPositioners.get(i);
|
||||
|
||||
switch(positioner.onCalculateBounds(task, layout, activity, options, mTmpCurrent,
|
||||
mTmpResult)) {
|
||||
case RESULT_SKIP:
|
||||
// Do not apply any results when we are told to skip
|
||||
continue;
|
||||
case RESULT_DONE:
|
||||
// Set result and return immediately.
|
||||
result.set(mTmpResult);
|
||||
return;
|
||||
case RESULT_CONTINUE:
|
||||
// Set result and continue
|
||||
result.set(mTmpResult);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for laying out a task.
|
||||
* @return {@code true} if bounds were set on the task. {@code false} otherwise.
|
||||
*/
|
||||
boolean layoutTask(TaskRecord task, WindowLayout layout) {
|
||||
calculateBounds(task, layout, null /*activity*/, null /*options*/, mTmpRect);
|
||||
|
||||
if (mTmpRect.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
task.updateOverrideConfiguration(mTmpRect);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a positioner to participate in future bounds calculation. Note that the last registered
|
||||
* {@link LaunchingBoundsPositioner} will be the first to calculate the bounds.
|
||||
*/
|
||||
void registerPositioner(LaunchingBoundsPositioner positioner) {
|
||||
if (mPositioners.contains(positioner)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPositioners.add(positioner);
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface implemented by those wanting to participate in bounds calculation.
|
||||
*/
|
||||
interface LaunchingBoundsPositioner {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
|
||||
@interface Result {}
|
||||
|
||||
// Returned when the positioner does not want to influence the bounds calculation
|
||||
int RESULT_SKIP = 0;
|
||||
// Returned when the positioner has changed the bounds and would like its results to be the
|
||||
// final bounds applied.
|
||||
int RESULT_DONE = 1;
|
||||
// Returned when the positioner has changed the bounds but is okay with other positioners
|
||||
// influencing the bounds.
|
||||
int RESULT_CONTINUE = 2;
|
||||
|
||||
/**
|
||||
* Called when asked to calculate bounds.
|
||||
* @param task The {@link TaskRecord} currently being positioned.
|
||||
* @param layout The specified {@link WindowLayout}.
|
||||
* @param activity The {@link ActivityRecord} currently being positioned.
|
||||
* @param options The {@link ActivityOptions} specified for the activity.
|
||||
* @param current The current bounds. This can differ from the initial bounds as it
|
||||
* represents the modified bounds up to this point.
|
||||
* @param result The {@link Rect} which the positioner should return its modified bounds.
|
||||
* Any merging of the current bounds should be already applied to this
|
||||
* value as well before returning.
|
||||
* @return A {@link Result} representing the result of the bounds calculation.
|
||||
*/
|
||||
@Result
|
||||
int onCalculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
|
||||
ActivityOptions options, Rect current, Rect result);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ 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.app.ActivityOptions;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
@@ -36,8 +36,10 @@ import java.util.ArrayList;
|
||||
* 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.
|
||||
*
|
||||
* Note that the only gravities of concern are the corners and the center.
|
||||
*/
|
||||
class LaunchingTaskPositioner {
|
||||
class LaunchingTaskPositioner implements LaunchingBoundsController.LaunchingBoundsPositioner {
|
||||
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.
|
||||
@@ -74,44 +76,50 @@ class LaunchingTaskPositioner {
|
||||
* 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.
|
||||
* @param windowLayout Optional information from the client about how it would like to be sized
|
||||
* and positioned.
|
||||
*/
|
||||
void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
|
||||
@Nullable ActivityInfo.WindowLayout windowLayout) {
|
||||
@Override
|
||||
public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
|
||||
ActivityRecord activity, ActivityOptions options, Rect current, Rect result) {
|
||||
// We can only apply positioning if we're in a freeform stack.
|
||||
if (task == null || task.getStack() == null || !task.inFreeformWindowingMode()) {
|
||||
return RESULT_SKIP;
|
||||
}
|
||||
|
||||
final ArrayList<TaskRecord> tasks = task.getStack().getAllTasks();
|
||||
|
||||
updateAvailableRect(task, mAvailableRect);
|
||||
|
||||
if (windowLayout == null) {
|
||||
positionCenter(task, tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
|
||||
getFreeformHeight(mAvailableRect));
|
||||
return;
|
||||
if (layout == null) {
|
||||
positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
|
||||
getFreeformHeight(mAvailableRect), result);
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
int width = getFinalWidth(windowLayout, mAvailableRect);
|
||||
int height = getFinalHeight(windowLayout, mAvailableRect);
|
||||
int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
||||
int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
|
||||
int width = getFinalWidth(layout, mAvailableRect);
|
||||
int height = getFinalHeight(layout, mAvailableRect);
|
||||
int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
||||
int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
if (verticalGravity == Gravity.TOP) {
|
||||
if (horizontalGravity == Gravity.RIGHT) {
|
||||
positionTopRight(task, tasks, mAvailableRect, width, height);
|
||||
positionTopRight(tasks, mAvailableRect, width, height, result);
|
||||
} else {
|
||||
positionTopLeft(task, tasks, mAvailableRect, width, height);
|
||||
positionTopLeft(tasks, mAvailableRect, width, height, result);
|
||||
}
|
||||
} else if (verticalGravity == Gravity.BOTTOM) {
|
||||
if (horizontalGravity == Gravity.RIGHT) {
|
||||
positionBottomRight(task, tasks, mAvailableRect, width, height);
|
||||
positionBottomRight(tasks, mAvailableRect, width, height, result);
|
||||
} else {
|
||||
positionBottomLeft(task, tasks, mAvailableRect, width, height);
|
||||
positionBottomLeft(tasks, mAvailableRect, width, height, result);
|
||||
}
|
||||
} 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: " + windowLayout.gravity
|
||||
Slog.w(TAG, "Received unsupported gravity: " + layout.gravity
|
||||
+ ", positioning in the center instead.");
|
||||
positionCenter(task, tasks, mAvailableRect, width, height);
|
||||
positionCenter(tasks, mAvailableRect, width, height, result);
|
||||
}
|
||||
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
|
||||
private void updateAvailableRect(TaskRecord task, Rect availableRect) {
|
||||
@@ -179,50 +187,50 @@ class LaunchingTaskPositioner {
|
||||
return height;
|
||||
}
|
||||
|
||||
private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
|
||||
Rect availableRect, int width, int height) {
|
||||
private void positionBottomLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
|
||||
int height, Rect result) {
|
||||
mTmpProposal.set(availableRect.left, availableRect.bottom - height,
|
||||
availableRect.left + width, availableRect.bottom);
|
||||
position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
|
||||
SHIFT_POLICY_HORIZONTAL_RIGHT);
|
||||
position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT,
|
||||
result);
|
||||
}
|
||||
|
||||
private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks,
|
||||
Rect availableRect, int width, int height) {
|
||||
private void positionBottomRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
|
||||
int height, Rect result) {
|
||||
mTmpProposal.set(availableRect.right - width, availableRect.bottom - height,
|
||||
availableRect.right, availableRect.bottom);
|
||||
position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
|
||||
SHIFT_POLICY_HORIZONTAL_LEFT);
|
||||
position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT,
|
||||
result);
|
||||
}
|
||||
|
||||
private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
|
||||
Rect availableRect, int width, int height) {
|
||||
private void positionTopLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
|
||||
int height, Rect result) {
|
||||
mTmpProposal.set(availableRect.left, availableRect.top,
|
||||
availableRect.left + width, availableRect.top + height);
|
||||
position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
|
||||
SHIFT_POLICY_HORIZONTAL_RIGHT);
|
||||
position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT,
|
||||
result);
|
||||
}
|
||||
|
||||
private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks,
|
||||
Rect availableRect, int width, int height) {
|
||||
private void positionTopRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
|
||||
int height, Rect result) {
|
||||
mTmpProposal.set(availableRect.right - width, availableRect.top,
|
||||
availableRect.right, availableRect.top + height);
|
||||
position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
|
||||
SHIFT_POLICY_HORIZONTAL_LEFT);
|
||||
position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT,
|
||||
result);
|
||||
}
|
||||
|
||||
private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks,
|
||||
Rect availableRect, int width, int height) {
|
||||
private void positionCenter(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
|
||||
int height, Rect result) {
|
||||
final int defaultFreeformLeft = getFreeformStartLeft(availableRect);
|
||||
final int defaultFreeformTop = getFreeformStartTop(availableRect);
|
||||
mTmpProposal.set(defaultFreeformLeft, defaultFreeformTop,
|
||||
defaultFreeformLeft + width, defaultFreeformTop + height);
|
||||
position(task, tasks, availableRect, mTmpProposal, ALLOW_RESTART,
|
||||
SHIFT_POLICY_DIAGONAL_DOWN);
|
||||
position(tasks, availableRect, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN,
|
||||
result);
|
||||
}
|
||||
|
||||
private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect availableRect,
|
||||
Rect proposal, boolean allowRestart, int shiftPolicy) {
|
||||
private void position(ArrayList<TaskRecord> tasks, Rect availableRect,
|
||||
Rect proposal, boolean allowRestart, int shiftPolicy, Rect result) {
|
||||
mTmpOriginal.set(proposal);
|
||||
boolean restarted = false;
|
||||
while (boundsConflict(proposal, tasks)) {
|
||||
@@ -252,7 +260,7 @@ class LaunchingTaskPositioner {
|
||||
break;
|
||||
}
|
||||
}
|
||||
task.updateOverrideConfiguration(proposal);
|
||||
result.set(proposal);
|
||||
}
|
||||
|
||||
private boolean shiftedTooFar(Rect start, Rect availableRect, int shiftPolicy) {
|
||||
|
||||
@@ -707,7 +707,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
|
||||
} else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
|
||||
Rect bounds = getLaunchBounds();
|
||||
if (bounds == null) {
|
||||
toStack.layoutTaskInStack(this, null);
|
||||
mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
|
||||
bounds = mBounds;
|
||||
}
|
||||
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
|
||||
@@ -2089,7 +2089,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
|
||||
if (mLastNonFullscreenBounds != null) {
|
||||
updateOverrideConfiguration(mLastNonFullscreenBounds);
|
||||
} else {
|
||||
inStack.layoutTaskInStack(this, null);
|
||||
mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
|
||||
}
|
||||
} else {
|
||||
updateOverrideConfiguration(inStack.mBounds);
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.app.ActivityOptions;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.filters.MediumTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
|
||||
|
||||
/**
|
||||
* Tests for exercising resizing bounds due to activity options.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* bit FrameworksServicesTests:com.android.server.am.LaunchingActivityPositionerTests
|
||||
*/
|
||||
@MediumTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LaunchingActivityPositionerTests extends ActivityTestsBase {
|
||||
private final ComponentName testActivityComponent =
|
||||
ComponentName.unflattenFromString("com.foo/.BarActivity");
|
||||
|
||||
private LaunchingActivityPositioner mPositioner;
|
||||
private ActivityManagerService mService;
|
||||
private ActivityStack mStack;
|
||||
private TaskRecord mTask;
|
||||
private ActivityRecord mActivity;
|
||||
|
||||
private Rect mCurrent;
|
||||
private Rect mResult;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mService = createActivityManagerService();
|
||||
mPositioner = new LaunchingActivityPositioner(mService.mStackSupervisor);
|
||||
mCurrent = new Rect();
|
||||
mResult = new Rect();
|
||||
|
||||
|
||||
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
|
||||
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
|
||||
mActivity = createActivity(mService, testActivityComponent, mTask);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSkippedInvocations() throws Exception {
|
||||
// No specified activity should be ignored
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
null /*activity*/, null /*options*/, mCurrent, mResult));
|
||||
|
||||
// No specified activity options should be ignored
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, null /*options*/, mCurrent, mResult));
|
||||
|
||||
// launch bounds specified should be ignored.
|
||||
final ActivityOptions options = ActivityOptions.makeBasic();
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
|
||||
// Non-resizeable records should be ignored
|
||||
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
|
||||
assertFalse(mActivity.isResizeable());
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
|
||||
// make record resizeable
|
||||
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
|
||||
assertTrue(mActivity.isResizeable());
|
||||
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
|
||||
// Does not support freeform
|
||||
mService.mSupportsFreeformWindowManagement = false;
|
||||
assertFalse(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options));
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
|
||||
mService.mSupportsFreeformWindowManagement = true;
|
||||
options.setLaunchBounds(new Rect());
|
||||
assertTrue(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options));
|
||||
|
||||
// Invalid bounds
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
options.setLaunchBounds(new Rect(0, 0, -1, -1));
|
||||
assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
|
||||
// Valid bounds should cause the positioner to be applied.
|
||||
options.setLaunchBounds(new Rect(0, 0, 100, 100));
|
||||
assertEquals(RESULT_DONE, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoundsExtraction() throws Exception {
|
||||
// Make activity resizeable and enable freeform mode.
|
||||
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
|
||||
mService.mSupportsFreeformWindowManagement = true;
|
||||
|
||||
ActivityOptions options = ActivityOptions.makeBasic();
|
||||
final Rect proposedBounds = new Rect(20, 30, 45, 40);
|
||||
options.setLaunchBounds(proposedBounds);
|
||||
|
||||
assertEquals(RESULT_DONE, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
|
||||
mActivity, options /*options*/, mCurrent, mResult));
|
||||
assertEquals(mResult, proposedBounds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.app.ActivityOptions;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.filters.MediumTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for exercising {@link LaunchingBoundsController}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* bit FrameworksServicesTests:com.android.server.am.LaunchingBoundsControllerTests
|
||||
*/
|
||||
@MediumTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LaunchingBoundsControllerTests extends ActivityTestsBase {
|
||||
private LaunchingBoundsController mController;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mController = new LaunchingBoundsController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures positioners further down the chain are not called when RESULT_DONE is returned.
|
||||
*/
|
||||
@Test
|
||||
public void testEarlyExit() {
|
||||
final LaunchingBoundsPositioner ignoredPositioner = mock(LaunchingBoundsPositioner.class);
|
||||
final LaunchingBoundsPositioner earlyExitPositioner =
|
||||
(task, layout, activity, options, current, result) -> RESULT_DONE;
|
||||
|
||||
mController.registerPositioner(ignoredPositioner);
|
||||
mController.registerPositioner(earlyExitPositioner);
|
||||
|
||||
mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
|
||||
null /*options*/, new Rect());
|
||||
verify(ignoredPositioner, never()).onCalculateBounds(any(), any(), any(), any(), any(),
|
||||
any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that positioners are called in the correct order.
|
||||
*/
|
||||
@Test
|
||||
public void testRegistration() {
|
||||
LaunchingBoundsPositioner earlyExitPositioner =
|
||||
new InstrumentedPositioner(RESULT_DONE, new Rect());
|
||||
|
||||
final LaunchingBoundsPositioner firstPositioner = spy(earlyExitPositioner);
|
||||
|
||||
mController.registerPositioner(firstPositioner);
|
||||
|
||||
mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
|
||||
null /*options*/, new Rect());
|
||||
verify(firstPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
|
||||
any());
|
||||
|
||||
final LaunchingBoundsPositioner secondPositioner = spy(earlyExitPositioner);
|
||||
|
||||
mController.registerPositioner(secondPositioner);
|
||||
|
||||
mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
|
||||
null /*options*/, new Rect());
|
||||
verify(firstPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
|
||||
any());
|
||||
verify(secondPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
|
||||
any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure positioners further down the registration chain are called.
|
||||
*/
|
||||
@Test
|
||||
public void testPassThrough() {
|
||||
final LaunchingBoundsPositioner positioner1 = mock(LaunchingBoundsPositioner.class);
|
||||
final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
|
||||
new Rect (0, 0, 30, 20));
|
||||
|
||||
mController.registerPositioner(positioner1);
|
||||
mController.registerPositioner(positioner2);
|
||||
|
||||
mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
|
||||
null /*options*/, new Rect());
|
||||
|
||||
verify(positioner1, times(1)).onCalculateBounds(any(), any(), any(), any(),
|
||||
eq(positioner2.getLaunchBounds()), any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures skipped results are not propagated.
|
||||
*/
|
||||
@Test
|
||||
public void testSkip() {
|
||||
final InstrumentedPositioner positioner1 =
|
||||
new InstrumentedPositioner(RESULT_SKIP, new Rect(0, 0, 10, 10));
|
||||
|
||||
|
||||
final InstrumentedPositioner positioner2 =
|
||||
new InstrumentedPositioner(RESULT_CONTINUE, new Rect(0, 0, 20, 30));
|
||||
|
||||
mController.registerPositioner(positioner1);
|
||||
mController.registerPositioner(positioner2);
|
||||
|
||||
final Rect resultBounds = new Rect();
|
||||
|
||||
mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
|
||||
null /*options*/, resultBounds);
|
||||
|
||||
assertEquals(resultBounds, positioner2.getLaunchBounds());
|
||||
}
|
||||
|
||||
public static class InstrumentedPositioner implements LaunchingBoundsPositioner {
|
||||
private int mReturnVal;
|
||||
private Rect mBounds;
|
||||
InstrumentedPositioner(int returnVal, Rect bounds) {
|
||||
mReturnVal = returnVal;
|
||||
mBounds = bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
|
||||
ActivityRecord activity, ActivityOptions options, Rect current, Rect result) {
|
||||
result.set(mBounds);
|
||||
return mReturnVal;
|
||||
}
|
||||
|
||||
Rect getLaunchBounds() {
|
||||
return mBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,12 @@
|
||||
package com.android.server.am;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ActivityInfo.WindowLayout;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.filters.MediumTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.Before;
|
||||
@@ -33,10 +30,11 @@ import org.junit.Test;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
|
||||
import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -48,16 +46,14 @@ import static org.junit.Assert.assertEquals;
|
||||
* Tests for exercising resizing bounds.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* bit FrameworksServicesTests:com.android.server.am.LaunchBoundsTests
|
||||
* bit FrameworksServicesTests:com.android.server.am.LaunchingTaskPositionerTests
|
||||
*/
|
||||
@MediumTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
public class LaunchingTaskPositionerTests extends ActivityTestsBase {
|
||||
private final ComponentName testActivityComponent =
|
||||
ComponentName.unflattenFromString("com.foo/.BarActivity");
|
||||
private final ComponentName testActivityComponent2 =
|
||||
ComponentName.unflattenFromString("com.foo/.BarActivity2");
|
||||
|
||||
private final static int STACK_WIDTH = 100;
|
||||
private final static int STACK_HEIGHT = 200;
|
||||
@@ -68,6 +64,11 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
private ActivityStack mStack;
|
||||
private TaskRecord mTask;
|
||||
|
||||
private LaunchingTaskPositioner mPositioner;
|
||||
|
||||
private Rect mCurrent;
|
||||
private Rect mResult;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
@@ -81,6 +82,11 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
// We must create the task after resizing to make sure it does not inherit the stack
|
||||
// dimensions on resize.
|
||||
mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
|
||||
|
||||
mPositioner = new LaunchingTaskPositioner();
|
||||
|
||||
mResult = new Rect();
|
||||
mCurrent = new Rect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,12 +107,9 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
*/
|
||||
@Test
|
||||
public void testLaunchNoWindowLayout() throws Exception {
|
||||
final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
|
||||
|
||||
mStack.layoutTaskInStack(mTask, null);
|
||||
|
||||
// We expect the task to be placed in the middle of the screen with margins applied.
|
||||
assertEquals(mTask.mBounds, expectedTaskBounds);
|
||||
assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask, null /*layout*/,
|
||||
null /*record*/, null /*options*/, mCurrent, mResult));
|
||||
assertEquals(getDefaultBounds(Gravity.NO_GRAVITY), mResult);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,11 +119,10 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
*/
|
||||
@Test
|
||||
public void testlaunchEmptyWindowLayout() throws Exception {
|
||||
final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
|
||||
|
||||
WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
|
||||
mStack.layoutTaskInStack(mTask, layout);
|
||||
assertEquals(mTask.mBounds, expectedTaskBounds);
|
||||
assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask,
|
||||
new WindowLayout(0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0), null /*activity*/,
|
||||
null /*options*/, mCurrent, mResult));
|
||||
assertEquals(mResult, getDefaultBounds(Gravity.NO_GRAVITY));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,9 +151,15 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
}
|
||||
|
||||
private void testGravity(int gravity) {
|
||||
final WindowLayout gravityLayout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
|
||||
mStack.layoutTaskInStack(mTask, gravityLayout);
|
||||
assertEquals(mTask.mBounds, getDefaultBounds(gravity));
|
||||
try {
|
||||
assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask,
|
||||
new WindowLayout(0, 0, 0, 0, gravity, 0, 0), null /*activity*/,
|
||||
null /*options*/, mCurrent, mResult));
|
||||
assertEquals(mResult, getDefaultBounds(gravity));
|
||||
} finally {
|
||||
mCurrent.setEmpty();
|
||||
mResult.setEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,7 +182,7 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
|
||||
|
||||
// layout first task
|
||||
mStack.layoutTaskInStack(mTask, layout /*windowLayout*/);
|
||||
mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(mTask, layout);
|
||||
|
||||
// Second task will be laid out on top of the first so starting bounds is the same.
|
||||
final Rect expectedBounds = new Rect(mTask.mBounds);
|
||||
@@ -192,7 +200,9 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
mStack);
|
||||
|
||||
// layout second task
|
||||
mStack.layoutTaskInStack(secondTask, layout /*windowLayout*/);
|
||||
assertEquals(RESULT_CONTINUE,
|
||||
mPositioner.onCalculateBounds(secondTask, layout, null /*activity*/,
|
||||
null /*options*/, mCurrent, mResult));
|
||||
|
||||
if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
|
||||
|| (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
|
||||
@@ -207,7 +217,7 @@ public class LaunchBoundsTests extends ActivityTestsBase {
|
||||
LaunchingTaskPositioner.getVerticalStep(mStack.mBounds));
|
||||
}
|
||||
|
||||
assertEquals(secondTask.mBounds, expectedBounds);
|
||||
assertEquals(mResult, expectedBounds);
|
||||
} finally {
|
||||
// Remove task and activity to prevent influencing future tests
|
||||
if (activity != null) {
|
||||
Reference in New Issue
Block a user