1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.server.am; 18 19import android.content.pm.ActivityInfo.WindowLayout; 20import android.graphics.Rect; 21import android.platform.test.annotations.Presubmit; 22import android.support.test.filters.MediumTest; 23import android.support.test.runner.AndroidJUnit4; 24 25import android.view.Gravity; 26 27import org.junit.runner.RunWith; 28import org.junit.Before; 29import org.junit.Test; 30 31import org.mockito.invocation.InvocationOnMock; 32 33import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 34import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 35 36import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; 37 38import static org.mockito.Mockito.any; 39import static org.mockito.Mockito.mock; 40import static org.mockito.Mockito.when; 41import static org.mockito.Mockito.doAnswer; 42import static org.junit.Assert.assertEquals; 43 44 45/** 46 * Tests for exercising resizing task bounds. 47 * 48 * Build/Install/Run: 49 * atest FrameworksServicesTests:TaskLaunchParamsModifierTests 50 */ 51@MediumTest 52@Presubmit 53@RunWith(AndroidJUnit4.class) 54public class TaskLaunchParamsModifierTests extends ActivityTestsBase { 55 private final static int STACK_WIDTH = 100; 56 private final static int STACK_HEIGHT = 200; 57 58 private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT); 59 60 private ActivityManagerService mService; 61 private ActivityStack mStack; 62 private TaskRecord mTask; 63 64 private TaskLaunchParamsModifier mPositioner; 65 66 private LaunchParamsController.LaunchParams mCurrent; 67 private LaunchParamsController.LaunchParams mResult; 68 69 @Before 70 @Override 71 public void setUp() throws Exception { 72 super.setUp(); 73 74 mService = createActivityManagerService(); 75 mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( 76 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); 77 mStack.requestResize(STACK_BOUNDS); 78 79 // We must create the task after resizing to make sure it does not inherit the stack 80 // dimensions on resize. 81 mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); 82 83 mPositioner = new TaskLaunchParamsModifier(); 84 85 mResult = new LaunchParamsController.LaunchParams(); 86 mCurrent = new LaunchParamsController.LaunchParams(); 87 } 88 89 /** 90 * Ensures that the setup bounds are set as expected with the stack bounds set and the task 91 * bounds still {@code null}. 92 * @throws Exception 93 */ 94 @Test 95 public void testInitialBounds() throws Exception { 96 assertEquals(mStack.getOverrideBounds(), STACK_BOUNDS); 97 assertEquals(mTask.getOverrideBounds(), new Rect()); 98 } 99 100 /** 101 * Ensures that a task positioned with no {@link WindowLayout} receives the default launch 102 * position. 103 * @throws Exception 104 */ 105 @Test 106 public void testLaunchNoWindowLayout() throws Exception { 107 assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, null /*layout*/, 108 null /*record*/, null /*source*/, null /*options*/, mCurrent, mResult)); 109 assertEquals(getDefaultBounds(Gravity.NO_GRAVITY), mResult.mBounds); 110 } 111 112 /** 113 * Ensures that a task positioned with an empty {@link WindowLayout} receives the default launch 114 * position. 115 * @throws Exception 116 */ 117 @Test 118 public void testlaunchEmptyWindowLayout() throws Exception { 119 assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, 120 new WindowLayout(0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0), null /*activity*/, 121 null /*source*/, null /*options*/, mCurrent, mResult)); 122 assertEquals(mResult.mBounds, getDefaultBounds(Gravity.NO_GRAVITY)); 123 } 124 125 /** 126 * Ensures that a task positioned with a {@link WindowLayout} gravity specified is positioned 127 * according to specification. 128 * @throws Exception 129 */ 130 @Test 131 public void testlaunchWindowLayoutGravity() throws Exception { 132 // Unspecified gravity should be ignored 133 testGravity(Gravity.NO_GRAVITY); 134 135 // Unsupported gravity should be ignored 136 testGravity(Gravity.LEFT); 137 testGravity(Gravity.RIGHT); 138 139 // Test defaults for vertical gravities 140 testGravity(Gravity.TOP); 141 testGravity(Gravity.BOTTOM); 142 143 // Test corners 144 testGravity(Gravity.TOP | Gravity.LEFT); 145 testGravity(Gravity.TOP | Gravity.RIGHT); 146 testGravity(Gravity.BOTTOM | Gravity.LEFT); 147 testGravity(Gravity.BOTTOM | Gravity.RIGHT); 148 } 149 150 private void testGravity(int gravity) { 151 try { 152 assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, 153 new WindowLayout(0, 0, 0, 0, gravity, 0, 0), null /*activity*/, 154 null /*source*/, null /*options*/, mCurrent, mResult)); 155 assertEquals(mResult.mBounds, getDefaultBounds(gravity)); 156 } finally { 157 mCurrent.reset(); 158 mResult.reset(); 159 } 160 } 161 162 /** 163 * Ensures that a task which causes a conflict with another task when positioned is adjusted as 164 * expected. 165 * @throws Exception 166 */ 167 @Test 168 public void testLaunchWindowCenterConflict() throws Exception { 169 testConflict(Gravity.NO_GRAVITY); 170 testConflict(Gravity.TOP); 171 testConflict(Gravity.BOTTOM); 172 testConflict(Gravity.TOP | Gravity.LEFT); 173 testConflict(Gravity.TOP | Gravity.RIGHT); 174 testConflict(Gravity.BOTTOM | Gravity.LEFT); 175 testConflict(Gravity.BOTTOM | Gravity.RIGHT); 176 } 177 178 private void testConflict(int gravity) { 179 final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0); 180 181 // layout first task 182 mService.mStackSupervisor.getLaunchParamsController().layoutTask(mTask, layout); 183 184 // Second task will be laid out on top of the first so starting bounds is the same. 185 final Rect expectedBounds = new Rect(mTask.getOverrideBounds()); 186 187 ActivityRecord activity = null; 188 TaskRecord secondTask = null; 189 190 // wrap with try/finally to ensure cleanup of activity/stack. 191 try { 192 // empty tasks are ignored in conflicts 193 activity = new ActivityBuilder(mService).setTask(mTask).build(); 194 195 // Create secondary task 196 secondTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); 197 198 // layout second task 199 assertEquals(RESULT_CONTINUE, 200 mPositioner.onCalculate(secondTask, layout, null /*activity*/, 201 null /*source*/, null /*options*/, mCurrent, mResult)); 202 203 if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT) 204 || (gravity & (Gravity.BOTTOM | Gravity.RIGHT)) 205 == (Gravity.BOTTOM | Gravity.RIGHT)) { 206 expectedBounds.offset(-TaskLaunchParamsModifier.getHorizontalStep( 207 mStack.getOverrideBounds()), 0); 208 } else if ((gravity & Gravity.TOP) == Gravity.TOP 209 || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { 210 expectedBounds.offset( 211 TaskLaunchParamsModifier.getHorizontalStep(mStack.getOverrideBounds()), 0); 212 } else { 213 expectedBounds.offset( 214 TaskLaunchParamsModifier.getHorizontalStep(mStack.getOverrideBounds()), 215 TaskLaunchParamsModifier.getVerticalStep(mStack.getOverrideBounds())); 216 } 217 218 assertEquals(mResult.mBounds, expectedBounds); 219 } finally { 220 // Remove task and activity to prevent influencing future tests 221 if (activity != null) { 222 mTask.removeActivity(activity); 223 } 224 225 if (secondTask != null) { 226 mStack.removeTask(secondTask, "cleanup", ActivityStack.REMOVE_TASK_MODE_DESTROYING); 227 } 228 } 229 } 230 231 private Rect getDefaultBounds(int gravity) { 232 final Rect bounds = new Rect(); 233 bounds.set(mStack.getOverrideBounds()); 234 235 final int verticalInset = 236 TaskLaunchParamsModifier.getFreeformStartTop(mStack.getOverrideBounds()); 237 final int horizontalInset = 238 TaskLaunchParamsModifier.getFreeformStartLeft(mStack.getOverrideBounds()); 239 240 bounds.inset(horizontalInset, verticalInset); 241 242 if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)) { 243 bounds.offsetTo(horizontalInset * 2, 0); 244 } else if ((gravity & Gravity.TOP) == Gravity.TOP) { 245 bounds.offsetTo(0, 0); 246 } else if ((gravity & (Gravity.BOTTOM | Gravity.RIGHT)) 247 == (Gravity.BOTTOM | Gravity.RIGHT)) { 248 bounds.offsetTo(horizontalInset * 2, verticalInset * 2); 249 } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { 250 bounds.offsetTo(0, verticalInset * 2); 251 } 252 253 return bounds; 254 } 255} 256