Loading services/core/java/com/android/server/am/ActivityStack.java +4 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); Loading services/core/java/com/android/server/am/LaunchingTaskPositioner.java +54 −27 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } /** Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java 0 → 100644 +245 −0 Original line number Diff line number Diff line /* * 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; } } Loading
services/core/java/com/android/server/am/ActivityStack.java +4 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); Loading
services/core/java/com/android/server/am/LaunchingTaskPositioner.java +54 −27 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } /** Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java 0 → 100644 +245 −0 Original line number Diff line number Diff line /* * 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; } }