1 /* 2 * Copyright (C) 2020 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 17 package com.android.server.wm; 18 19 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 22 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 23 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 24 import static android.view.Surface.ROTATION_90; 25 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 26 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 27 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 28 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 29 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 30 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; 31 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; 32 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; 33 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; 34 35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 36 import static com.android.server.wm.SizeCompatTests.prepareLimitedBounds; 37 import static com.android.server.wm.SizeCompatTests.prepareUnresizable; 38 import static com.android.server.wm.SizeCompatTests.rotateDisplay; 39 40 import static com.google.common.truth.Truth.assertThat; 41 42 import static org.mockito.ArgumentMatchers.any; 43 import static org.mockito.Mockito.doReturn; 44 import static org.mockito.Mockito.never; 45 import static org.mockito.Mockito.verify; 46 47 import android.content.res.Configuration; 48 import android.graphics.Rect; 49 import android.os.Binder; 50 import android.platform.test.annotations.Presubmit; 51 import android.view.Display; 52 53 import androidx.test.filters.SmallTest; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 59 import java.util.ArrayList; 60 import java.util.List; 61 62 /** 63 * Tests for the Dual DisplayAreaGroup device behavior. 64 * 65 * Build/Install/Run: 66 * atest WmTests:DualDisplayAreaGroupPolicyTest 67 */ 68 @SmallTest 69 @Presubmit 70 @RunWith(WindowTestRunner.class) 71 public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { 72 private static final int FEATURE_FIRST_ROOT = FEATURE_VENDOR_FIRST; 73 private static final int FEATURE_FIRST_TASK_CONTAINER = FEATURE_DEFAULT_TASK_CONTAINER; 74 private static final int FEATURE_SECOND_ROOT = FEATURE_VENDOR_FIRST + 1; 75 private static final int FEATURE_SECOND_TASK_CONTAINER = FEATURE_VENDOR_FIRST + 2; 76 77 private DualDisplayContent mDisplay; 78 private DisplayAreaGroup mFirstRoot; 79 private DisplayAreaGroup mSecondRoot; 80 private TaskDisplayArea mFirstTda; 81 private TaskDisplayArea mSecondTda; 82 private Task mFirstTask; 83 private Task mSecondTask; 84 private ActivityRecord mFirstActivity; 85 private ActivityRecord mSecondActivity; 86 87 @Before setUp()88 public void setUp() { 89 // Let the Display to be created with the DualDisplay policy. 90 final DisplayAreaPolicy.Provider policyProvider = new DualDisplayTestPolicyProvider(); 91 doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); 92 93 // Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait). 94 mDisplay = new DualDisplayContent.Builder(mAtm, 1920, 1200).build(); 95 mFirstRoot = mDisplay.mFirstRoot; 96 mSecondRoot = mDisplay.mSecondRoot; 97 mFirstTda = mDisplay.getTaskDisplayArea(FEATURE_FIRST_TASK_CONTAINER); 98 mSecondTda = mDisplay.getTaskDisplayArea(FEATURE_SECOND_TASK_CONTAINER); 99 mFirstTask = new TaskBuilder(mSupervisor) 100 .setTaskDisplayArea(mFirstTda) 101 .setCreateActivity(true) 102 .build() 103 .getBottomMostTask(); 104 mSecondTask = new TaskBuilder(mSupervisor) 105 .setTaskDisplayArea(mSecondTda) 106 .setCreateActivity(true) 107 .build() 108 .getBottomMostTask(); 109 mFirstActivity = mFirstTask.getTopNonFinishingActivity(); 110 mSecondActivity = mSecondTask.getTopNonFinishingActivity(); 111 112 spyOn(mDisplay); 113 spyOn(mFirstRoot); 114 spyOn(mSecondRoot); 115 } 116 117 @Test testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest()118 public void testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest() { 119 mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 120 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 121 122 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 123 124 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); 125 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 126 127 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); 128 129 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 130 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 131 } 132 133 @Test testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea()134 public void testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea() { 135 mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 136 mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 137 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 138 139 // Second TDA is not focused, so Display won't get the request 140 prepareUnresizable(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE); 141 142 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 143 144 // First TDA is focused, so Display gets the request 145 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 146 147 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); 148 } 149 150 @Test testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange()151 public void testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange() { 152 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 153 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 154 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 155 156 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 157 158 verify(mFirstRoot).onDescendantOrientationChanged(any()); 159 verify(mDisplay, never()).onDescendantOrientationChanged(any()); 160 } 161 162 @Test testLaunchPortraitApp_fillsDisplayAreaGroup()163 public void testLaunchPortraitApp_fillsDisplayAreaGroup() { 164 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 165 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 166 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 167 168 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); 169 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 170 final Rect taskBounds = new Rect(mFirstTask.getBounds()); 171 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 172 173 // DAG is portrait (860x1200), so Task and Activity fill DAG. 174 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 175 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 176 assertThat(taskBounds).isEqualTo(dagBounds); 177 assertThat(activityBounds).isEqualTo(taskBounds); 178 } 179 180 @Test testLaunchPortraitApp_sizeCompatAfterRotation()181 public void testLaunchPortraitApp_sizeCompatAfterRotation() { 182 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 183 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 184 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 185 186 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); 187 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 188 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 189 190 rotateDisplay(mDisplay, ROTATION_90); 191 final Rect newDagBounds = new Rect(mFirstRoot.getBounds()); 192 final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); 193 final Rect activitySizeCompatBounds = new Rect(mFirstActivity.getBounds()); 194 final Rect activityConfigBounds = 195 new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds()); 196 197 // DAG is landscape (1200x860), no fixed orientation letterbox 198 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 199 assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); 200 assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); 201 assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); 202 assertThat(newTaskBounds).isEqualTo(newDagBounds); 203 204 // Activity config bounds is unchanged, size compat bounds is (860x[860x860/1200=616]) 205 assertThat(mFirstActivity.getSizeCompatScale()).isLessThan(1f); 206 assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width()); 207 assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height()); 208 assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height()); 209 assertThat(activitySizeCompatBounds.width()).isEqualTo( 210 newTaskBounds.height() * newTaskBounds.height() / newTaskBounds.width()); 211 } 212 213 @Test testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup()214 public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() { 215 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 216 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 217 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 218 219 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 220 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 221 final Rect taskBounds = new Rect(mFirstTask.getBounds()); 222 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 223 224 // DAG is portrait (860x1200), and activity is letterboxed for fixed orientation 225 // (860x[860x860/1200=616]). Task fills DAG. 226 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue(); 227 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 228 assertThat(taskBounds).isEqualTo(dagBounds); 229 assertThat(activityBounds.width()).isEqualTo(dagBounds.width()); 230 assertThat(activityBounds.height()) 231 .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height()); 232 } 233 234 @Test testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation()235 public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { 236 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 237 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 238 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 239 240 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 241 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 242 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 243 244 rotateDisplay(mDisplay, ROTATION_90); 245 final Rect newDagBounds = new Rect(mFirstRoot.getBounds()); 246 final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); 247 final Rect newActivityBounds = new Rect(mFirstActivity.getBounds()); 248 249 // DAG is landscape (1200x860), no fixed orientation letterbox 250 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 251 assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); 252 assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); 253 assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); 254 assertThat(newTaskBounds).isEqualTo(newDagBounds); 255 256 // Because we don't scale up, there is no size compat bounds and app bounds is the same as 257 // the previous bounds. 258 assertThat(mFirstActivity.hasSizeCompatBounds()).isFalse(); 259 assertThat(newActivityBounds.width()).isEqualTo(activityBounds.width()); 260 assertThat(newActivityBounds.height()).isEqualTo(activityBounds.height()); 261 } 262 263 @Test testPlaceImeContainer_reparentToTargetDisplayAreaGroup()264 public void testPlaceImeContainer_reparentToTargetDisplayAreaGroup() { 265 setupImeWindow(); 266 final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); 267 final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD); 268 269 // By default, the ime container is attached to DC as defined in DAPolicy. 270 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); 271 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 272 273 final WindowState firstActivityWin = 274 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, 275 "firstActivityWin"); 276 spyOn(firstActivityWin); 277 final WindowState secondActivityWin = 278 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity, 279 "firstActivityWin"); 280 spyOn(secondActivityWin); 281 282 // firstActivityWin should be the target 283 doReturn(true).when(firstActivityWin).canBeImeTarget(); 284 doReturn(false).when(secondActivityWin).canBeImeTarget(); 285 286 WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 287 288 assertThat(imeTarget).isEqualTo(firstActivityWin); 289 verify(mFirstRoot).placeImeContainer(imeContainer); 290 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mFirstRoot); 291 assertThat(imeContainer.getParent().asDisplayArea().mFeatureId) 292 .isEqualTo(FEATURE_IME_PLACEHOLDER); 293 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isNull(); 294 assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 295 assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isNull(); 296 297 // secondActivityWin should be the target 298 doReturn(false).when(firstActivityWin).canBeImeTarget(); 299 doReturn(true).when(secondActivityWin).canBeImeTarget(); 300 301 imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 302 303 assertThat(imeTarget).isEqualTo(secondActivityWin); 304 verify(mSecondRoot).placeImeContainer(imeContainer); 305 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondRoot); 306 assertThat(imeContainer.getParent().asDisplayArea().mFeatureId) 307 .isEqualTo(FEATURE_IME_PLACEHOLDER); 308 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isNull(); 309 assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isNull(); 310 assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 311 } 312 313 @Test testPlaceImeContainer_hidesImeWhenParentChanges()314 public void testPlaceImeContainer_hidesImeWhenParentChanges() { 315 setupImeWindow(); 316 final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); 317 final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD); 318 final WindowState firstActivityWin = 319 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, 320 "firstActivityWin"); 321 spyOn(firstActivityWin); 322 final WindowState secondActivityWin = 323 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity, 324 "secondActivityWin"); 325 spyOn(secondActivityWin); 326 327 // firstActivityWin should be the target 328 doReturn(true).when(firstActivityWin).canBeImeTarget(); 329 doReturn(false).when(secondActivityWin).canBeImeTarget(); 330 331 WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 332 assertThat(imeTarget).isEqualTo(firstActivityWin); 333 verify(mFirstRoot).placeImeContainer(imeContainer); 334 335 // secondActivityWin should be the target 336 doReturn(false).when(firstActivityWin).canBeImeTarget(); 337 doReturn(true).when(secondActivityWin).canBeImeTarget(); 338 339 spyOn(mDisplay.mInputMethodWindow); 340 imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 341 342 assertThat(imeTarget).isEqualTo(secondActivityWin); 343 verify(mSecondRoot).placeImeContainer(imeContainer); 344 // verify hide() was called on InputMethodWindow. 345 verify(mDisplay.mInputMethodWindow).hide(false /* doAnimation */, false /* requestAnim */); 346 } 347 348 @Test testResizableFixedOrientationApp_fixedOrientationLetterboxing()349 public void testResizableFixedOrientationApp_fixedOrientationLetterboxing() { 350 mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 351 mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 352 353 // Launch portrait on first DAG 354 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 355 prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT, 356 false /* isUnresizable */); 357 358 // Display in landscape (as opposite to DAG), first DAG and activity in portrait 359 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 360 assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 361 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 362 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 363 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 364 365 // Launch portrait on second DAG 366 mDisplay.onLastFocusedTaskDisplayAreaChanged(mSecondTda); 367 prepareLimitedBounds(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE, 368 false /* isUnresizable */); 369 370 // Display in portrait (as opposite to DAG), first DAG and activity in landscape 371 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); 372 assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 373 assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 374 assertThat(mSecondActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 375 assertThat(mSecondActivity.inSizeCompatMode()).isFalse(); 376 377 // First activity is letterboxed in portrait as requested. 378 assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 379 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 380 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue(); 381 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 382 383 } 384 setupImeWindow()385 private void setupImeWindow() { 386 final WindowState imeWindow = createWindow(null /* parent */, 387 TYPE_INPUT_METHOD, mDisplay, "mImeWindow"); 388 imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; 389 mDisplay.mInputMethodWindow = imeWindow; 390 } 391 tokenOfType(int type)392 private WindowToken tokenOfType(int type) { 393 return new WindowToken.Builder(mWm, new Binder(), type) 394 .setDisplayContent(mDisplay).build(); 395 } 396 397 /** Display with two {@link DisplayAreaGroup}. Each of them take half of the screen. */ 398 static class DualDisplayContent extends TestDisplayContent { 399 final DisplayAreaGroup mFirstRoot; 400 final DisplayAreaGroup mSecondRoot; 401 final Rect mLastDisplayBounds; 402 403 /** Please use the {@link Builder} to create. */ DualDisplayContent(RootWindowContainer rootWindowContainer, Display display)404 DualDisplayContent(RootWindowContainer rootWindowContainer, 405 Display display) { 406 super(rootWindowContainer, display); 407 408 mFirstRoot = getGroupRoot(FEATURE_FIRST_ROOT); 409 mSecondRoot = getGroupRoot(FEATURE_SECOND_ROOT); 410 mLastDisplayBounds = new Rect(getBounds()); 411 updateDisplayAreaGroupBounds(); 412 } 413 getGroupRoot(int rootFeatureId)414 DisplayAreaGroup getGroupRoot(int rootFeatureId) { 415 DisplayArea da = getDisplayArea(rootFeatureId); 416 assertThat(da).isInstanceOf(DisplayAreaGroup.class); 417 return (DisplayAreaGroup) da; 418 } 419 getTaskDisplayArea(int tdaFeatureId)420 TaskDisplayArea getTaskDisplayArea(int tdaFeatureId) { 421 DisplayArea da = getDisplayArea(tdaFeatureId); 422 assertThat(da).isInstanceOf(TaskDisplayArea.class); 423 return (TaskDisplayArea) da; 424 } 425 getDisplayArea(int featureId)426 DisplayArea getDisplayArea(int featureId) { 427 final DisplayArea displayArea = 428 getItemFromDisplayAreas(da -> da.mFeatureId == featureId ? da : null); 429 assertThat(displayArea).isNotNull(); 430 return displayArea; 431 } 432 433 @Override onConfigurationChanged(Configuration newParentConfig)434 public void onConfigurationChanged(Configuration newParentConfig) { 435 super.onConfigurationChanged(newParentConfig); 436 437 final Rect curBounds = getBounds(); 438 if (mLastDisplayBounds != null && !mLastDisplayBounds.equals(curBounds)) { 439 mLastDisplayBounds.set(curBounds); 440 updateDisplayAreaGroupBounds(); 441 } 442 } 443 444 /** Updates first and second {@link DisplayAreaGroup} to take half of the screen. */ updateDisplayAreaGroupBounds()445 private void updateDisplayAreaGroupBounds() { 446 if (mFirstRoot == null || mSecondRoot == null) { 447 return; 448 } 449 450 final Rect bounds = mLastDisplayBounds; 451 Rect groupBounds1, groupBounds2; 452 if (bounds.width() >= bounds.height()) { 453 groupBounds1 = new Rect(bounds.left, bounds.top, 454 (bounds.right + bounds.left) / 2, bounds.bottom); 455 456 groupBounds2 = new Rect((bounds.right + bounds.left) / 2, bounds.top, 457 bounds.right, bounds.bottom); 458 } else { 459 groupBounds1 = new Rect(bounds.left, bounds.top, 460 bounds.right, (bounds.top + bounds.bottom) / 2); 461 462 groupBounds2 = new Rect(bounds.left, 463 (bounds.top + bounds.bottom) / 2, bounds.right, bounds.bottom); 464 } 465 mFirstRoot.setBounds(groupBounds1); 466 mSecondRoot.setBounds(groupBounds2); 467 } 468 469 static class Builder extends TestDisplayContent.Builder { 470 Builder(ActivityTaskManagerService service, int width, int height)471 Builder(ActivityTaskManagerService service, int width, int height) { 472 super(service, width, height); 473 } 474 475 @Override createInternal(Display display)476 TestDisplayContent createInternal(Display display) { 477 return new DualDisplayContent(mService.mRootWindowContainer, display); 478 } 479 build()480 DualDisplayContent build() { 481 return (DualDisplayContent) super.build(); 482 } 483 } 484 } 485 486 /** Policy to create a dual {@link DisplayAreaGroup} policy in test. */ 487 static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider { 488 489 @Override instantiate(WindowManagerService wmService, DisplayContent content, RootDisplayArea root, DisplayArea.Tokens imeContainer)490 public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, 491 RootDisplayArea root, DisplayArea.Tokens imeContainer) { 492 // Root 493 // Include FEATURE_WINDOWED_MAGNIFICATION because it will be used as the screen rotation 494 // layer 495 DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = 496 new DisplayAreaPolicyBuilder.HierarchyBuilder(root) 497 .setImeContainer(imeContainer) 498 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 499 wmService.mPolicy, 500 "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION) 501 .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 502 .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 503 .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) 504 .build()) 505 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 506 wmService.mPolicy, 507 "ImePlaceholder", FEATURE_IME_PLACEHOLDER) 508 .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) 509 .build()); 510 511 // First 512 final RootDisplayArea firstRoot = new DisplayAreaGroup(wmService, "FirstRoot", 513 FEATURE_FIRST_ROOT); 514 final TaskDisplayArea firstTaskDisplayArea = new TaskDisplayArea(content, wmService, 515 "FirstTaskDisplayArea", FEATURE_FIRST_TASK_CONTAINER); 516 final List<TaskDisplayArea> firstTdaList = new ArrayList<>(); 517 firstTdaList.add(firstTaskDisplayArea); 518 DisplayAreaPolicyBuilder.HierarchyBuilder firstHierarchy = 519 new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot) 520 .setTaskDisplayAreas(firstTdaList) 521 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 522 wmService.mPolicy, 523 "ImePlaceholder", FEATURE_IME_PLACEHOLDER) 524 .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) 525 .build()); 526 527 // Second 528 final RootDisplayArea secondRoot = new DisplayAreaGroup(wmService, "SecondRoot", 529 FEATURE_SECOND_ROOT); 530 final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(content, wmService, 531 "SecondTaskDisplayArea", FEATURE_SECOND_TASK_CONTAINER); 532 final List<TaskDisplayArea> secondTdaList = new ArrayList<>(); 533 secondTdaList.add(secondTaskDisplayArea); 534 DisplayAreaPolicyBuilder.HierarchyBuilder secondHierarchy = 535 new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot) 536 .setTaskDisplayAreas(secondTdaList) 537 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 538 wmService.mPolicy, 539 "ImePlaceholder", FEATURE_IME_PLACEHOLDER) 540 .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) 541 .build()); 542 543 return new DisplayAreaPolicyBuilder() 544 .setRootHierarchy(rootHierarchy) 545 .addDisplayAreaGroupHierarchy(firstHierarchy) 546 .addDisplayAreaGroupHierarchy(secondHierarchy) 547 .build(wmService); 548 } 549 } 550 } 551