Merge "Initial tests for launch bounds."
This commit is contained in:
@@ -110,6 +110,7 @@ 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;
|
||||
@@ -352,6 +353,7 @@ 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;
|
||||
@@ -503,7 +505,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
|
||||
mBounds = bounds != null ? new Rect(bounds) : null;
|
||||
mFullscreen = mBounds == null;
|
||||
if (mTaskPositioner != null) {
|
||||
mTaskPositioner.setDisplay(activityDisplay.mDisplay);
|
||||
activityDisplay.mDisplay.getSize(mTmpSize);
|
||||
mTaskPositioner.setDisplaySize(mTmpSize);
|
||||
mTaskPositioner.configure(mBounds);
|
||||
}
|
||||
onParentChanged();
|
||||
|
||||
@@ -24,8 +24,8 @@ 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 com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -75,31 +75,10 @@ class LaunchingTaskPositioner {
|
||||
private int mDefaultFreeformHeight;
|
||||
private int mDefaultFreeformStepHorizontal;
|
||||
private int mDefaultFreeformStepVertical;
|
||||
private int mDisplayWidth;
|
||||
private int mDisplayHeight;
|
||||
private final Point mDisplaySize = new Point();
|
||||
|
||||
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;
|
||||
void setDisplaySize(Point size) {
|
||||
mDisplaySize.set(size.x, size.y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +125,54 @@ class LaunchingTaskPositioner {
|
||||
}
|
||||
}
|
||||
|
||||
void configure(Rect availableSpace) {
|
||||
if (availableSpace == null) {
|
||||
mAvailableRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
|
||||
} else {
|
||||
mAvailableRect.set(availableSpace);
|
||||
}
|
||||
|
||||
mDefaultFreeformStartX = getFreeformStartLeft(mAvailableRect);
|
||||
mDefaultFreeformStartY = getFreeformStartTop(mAvailableRect);
|
||||
mDefaultFreeformWidth = getFreeformWidth(mAvailableRect);
|
||||
mDefaultFreeformHeight = getFreeformHeight(mAvailableRect);
|
||||
mDefaultFreeformStepHorizontal = getHorizontalStep(mAvailableRect);
|
||||
mDefaultFreeformStepVertical = getVerticalStep(mAvailableRect);
|
||||
mDefaultStartBoundsConfigurationSet = true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getFreeformStartLeft(Rect bounds) {
|
||||
return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getFreeformStartTop(Rect bounds) {
|
||||
return bounds.top + bounds.height() / MARGIN_SIZE_DENOMINATOR;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getFreeformWidth(Rect bounds) {
|
||||
return bounds.width() / WINDOW_SIZE_DENOMINATOR;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getFreeformHeight(Rect bounds) {
|
||||
return bounds.height() / WINDOW_SIZE_DENOMINATOR;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getHorizontalStep(Rect bounds) {
|
||||
return Math.max(bounds.width() / STEP_DENOMINATOR, MINIMAL_STEP);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getVerticalStep(Rect bounds) {
|
||||
return Math.max(bounds.height() / STEP_DENOMINATOR, MINIMAL_STEP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int getFinalWidth(ActivityInfo.WindowLayout windowLayout) {
|
||||
int width = mDefaultFreeformWidth;
|
||||
if (windowLayout.width > 0) {
|
||||
@@ -211,7 +238,7 @@ class LaunchingTaskPositioner {
|
||||
// Unfortunately there is already a task at that spot, so we need to look for some
|
||||
// other place.
|
||||
shiftStartingPoint(proposal, shiftPolicy);
|
||||
if (shiftedToFar(proposal, shiftPolicy)) {
|
||||
if (shiftedTooFar(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) {
|
||||
@@ -237,7 +264,7 @@ class LaunchingTaskPositioner {
|
||||
task.updateOverrideConfiguration(proposal);
|
||||
}
|
||||
|
||||
private boolean shiftedToFar(Rect start, int shiftPolicy) {
|
||||
private boolean shiftedTooFar(Rect start, int shiftPolicy) {
|
||||
switch (shiftPolicy) {
|
||||
case SHIFT_POLICY_HORIZONTAL_LEFT:
|
||||
return start.left < mAvailableRect.left;
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.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;
|
||||
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 org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for exercising resizing bounds.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* bit FrameworksServicesTests:com.android.server.am.LaunchBoundsTests
|
||||
*/
|
||||
@MediumTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LaunchBoundsTests 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;
|
||||
|
||||
private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT);
|
||||
|
||||
private ActivityManagerService mService;
|
||||
private ActivityStack mStack;
|
||||
private TaskRecord mTask;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mService = createActivityManagerService();
|
||||
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
|
||||
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
|
||||
mStack.requestResize(STACK_BOUNDS);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the setup bounds are set as expected with the stack bounds set and the task
|
||||
* bounds still {@code null}.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testInitialBounds() throws Exception {
|
||||
assertEquals(mStack.mBounds, STACK_BOUNDS);
|
||||
assertEquals(mTask.mBounds, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a task positioned with no {@link WindowLayout} receives the default launch
|
||||
* position.
|
||||
* @throws Exception
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a task positioned with an empty {@link WindowLayout} receives the default launch
|
||||
* position.
|
||||
* @throws Exception
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a task positioned with a {@link WindowLayout} gravity specified is positioned
|
||||
* according to specification.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testlaunchWindowLayoutGravity() throws Exception {
|
||||
// Unspecified gravity should be ignored
|
||||
testGravity(Gravity.NO_GRAVITY);
|
||||
|
||||
// Unsupported gravity should be ignored
|
||||
testGravity(Gravity.LEFT);
|
||||
testGravity(Gravity.RIGHT);
|
||||
|
||||
// Test defaults for vertical gravities
|
||||
testGravity(Gravity.TOP);
|
||||
testGravity(Gravity.BOTTOM);
|
||||
|
||||
// Test corners
|
||||
testGravity(Gravity.TOP | Gravity.LEFT);
|
||||
testGravity(Gravity.TOP | Gravity.RIGHT);
|
||||
testGravity(Gravity.BOTTOM | Gravity.LEFT);
|
||||
testGravity(Gravity.BOTTOM | Gravity.RIGHT);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a task which causes a conflict with another task when positioned is adjusted as
|
||||
* expected.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testLaunchWindowCenterConflict() throws Exception {
|
||||
testConflict(Gravity.NO_GRAVITY);
|
||||
testConflict(Gravity.TOP);
|
||||
testConflict(Gravity.BOTTOM);
|
||||
testConflict(Gravity.TOP | Gravity.LEFT);
|
||||
testConflict(Gravity.TOP | Gravity.RIGHT);
|
||||
testConflict(Gravity.BOTTOM | Gravity.LEFT);
|
||||
testConflict(Gravity.BOTTOM | Gravity.RIGHT);
|
||||
}
|
||||
|
||||
private void testConflict(int gravity) {
|
||||
final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
|
||||
|
||||
// layout first task
|
||||
mStack.layoutTaskInStack(mTask, layout /*windowLayout*/);
|
||||
|
||||
// Second task will be laid out on top of the first so starting bounds is the same.
|
||||
final Rect expectedBounds = new Rect(mTask.mBounds);
|
||||
|
||||
ActivityRecord activity = null;
|
||||
TaskRecord secondTask = null;
|
||||
|
||||
// wrap with try/finally to ensure cleanup of activity/stack.
|
||||
try {
|
||||
// empty tasks are ignored in conflicts
|
||||
activity = createActivity(mService, testActivityComponent, mTask);
|
||||
|
||||
// Create secondary task
|
||||
secondTask = createTask(mService.mStackSupervisor, testActivityComponent,
|
||||
mStack);
|
||||
|
||||
// layout second task
|
||||
mStack.layoutTaskInStack(secondTask, layout /*windowLayout*/);
|
||||
|
||||
if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
|
||||
|| (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
|
||||
== (Gravity.BOTTOM | Gravity.RIGHT)) {
|
||||
expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
|
||||
0);
|
||||
} else if ((gravity & Gravity.TOP) == Gravity.TOP
|
||||
|| (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
|
||||
expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), 0);
|
||||
} else {
|
||||
expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
|
||||
LaunchingTaskPositioner.getVerticalStep(mStack.mBounds));
|
||||
}
|
||||
|
||||
assertEquals(secondTask.mBounds, expectedBounds);
|
||||
} finally {
|
||||
// Remove task and activity to prevent influencing future tests
|
||||
if (activity != null) {
|
||||
mTask.removeActivity(activity);
|
||||
}
|
||||
|
||||
if (secondTask != null) {
|
||||
mStack.removeTask(secondTask, "cleanup", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Rect getDefaultBounds(int gravity) {
|
||||
final Rect bounds = new Rect();
|
||||
bounds.set(mStack.mBounds);
|
||||
|
||||
final int verticalInset = LaunchingTaskPositioner.getFreeformStartTop(mStack.mBounds);
|
||||
final int horizontalInset = LaunchingTaskPositioner.getFreeformStartLeft(mStack.mBounds);
|
||||
|
||||
bounds.inset(horizontalInset, verticalInset);
|
||||
|
||||
if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)) {
|
||||
bounds.offsetTo(horizontalInset * 2, 0);
|
||||
} else if ((gravity & Gravity.TOP) == Gravity.TOP) {
|
||||
bounds.offsetTo(0, 0);
|
||||
} else if ((gravity & (Gravity.BOTTOM | Gravity.RIGHT))
|
||||
== (Gravity.BOTTOM | Gravity.RIGHT)) {
|
||||
bounds.offsetTo(horizontalInset * 2, verticalInset * 2);
|
||||
} else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
|
||||
bounds.offsetTo(0, verticalInset * 2);
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user