1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.server.wm.display; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 25 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 26 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 27 import static android.server.wm.StateLogger.logE; 28 import static android.server.wm.WindowManagerState.STATE_RESUMED; 29 import static android.server.wm.WindowManagerState.dpToPx; 30 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY; 31 import static android.server.wm.app.Components.DIALOG_WHEN_LARGE_ACTIVITY; 32 import static android.server.wm.app.Components.LANDSCAPE_ORIENTATION_ACTIVITY; 33 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 34 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO; 35 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE; 36 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE; 37 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO; 38 import static android.server.wm.app.Components.NIGHT_MODE_ACTIVITY; 39 import static android.server.wm.app.Components.PORTRAIT_ORIENTATION_ACTIVITY; 40 import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY; 41 import static android.server.wm.app.Components.TEST_ACTIVITY; 42 import static android.server.wm.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY; 43 import static android.view.Surface.ROTATION_0; 44 import static android.view.Surface.ROTATION_180; 45 import static android.view.Surface.ROTATION_270; 46 import static android.view.Surface.ROTATION_90; 47 48 import static org.hamcrest.MatcherAssert.assertThat; 49 import static org.hamcrest.Matchers.lessThan; 50 import static org.junit.Assert.assertEquals; 51 import static org.junit.Assert.assertFalse; 52 import static org.junit.Assert.assertNotEquals; 53 import static org.junit.Assert.assertTrue; 54 import static org.junit.Assume.assumeFalse; 55 import static org.junit.Assume.assumeTrue; 56 57 import android.app.Activity; 58 import android.content.ComponentName; 59 import android.content.Context; 60 import android.graphics.Point; 61 import android.graphics.Rect; 62 import android.hardware.display.DisplayManager; 63 import android.os.Bundle; 64 import android.platform.test.annotations.Presubmit; 65 import android.server.wm.CommandSession.ActivitySession; 66 import android.server.wm.CommandSession.ActivitySessionClient; 67 import android.server.wm.CommandSession.ConfigInfo; 68 import android.server.wm.CommandSession.SizeInfo; 69 import android.server.wm.MultiDisplayTestBase; 70 import android.server.wm.RotationSession; 71 import android.server.wm.TestJournalProvider.TestJournalContainer; 72 import android.server.wm.WaitForValidActivityState; 73 import android.server.wm.WindowManagerState; 74 import android.view.Display; 75 import android.window.WindowContainerTransaction; 76 77 import org.junit.Test; 78 79 import java.util.function.Function; 80 81 /** 82 * Build/Install/Run: 83 * atest CtsWindowManagerDeviceDisplay:AppConfigurationTests 84 */ 85 @Presubmit 86 public class AppConfigurationTests extends MultiDisplayTestBase { 87 88 private static final int SMALL_WIDTH_DP = 426; 89 private static final int SMALL_HEIGHT_DP = 320; 90 91 /** 92 * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity 93 * has an updated size when the Activity is resized from fullscreen to docked state. 94 * 95 * The Activity handles configuration changes, so it will not be restarted between resizes. 96 * On Configuration changes, the Activity logs the Display size and Configuration width 97 * and heights. The values reported in fullscreen should be larger than those reported in 98 * docked state. 99 */ 100 @Test testConfigurationUpdatesWhenResizedFromFullscreen()101 public void testConfigurationUpdatesWhenResizedFromFullscreen() { 102 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 103 104 separateTestJournal(); 105 launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 106 final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 107 108 separateTestJournal(); 109 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 110 final SizeInfo dockedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 111 112 assertSizesAreSane(fullscreenSizes, dockedSizes); 113 } 114 115 /** 116 * Same as {@link #testConfigurationUpdatesWhenResizedFromFullscreen()} but resizing 117 * from docked state to fullscreen (reverse). 118 */ 119 @Test testConfigurationUpdatesWhenResizedFromDockedStack()120 public void testConfigurationUpdatesWhenResizedFromDockedStack() { 121 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 122 123 separateTestJournal(); 124 launchActivitiesInSplitScreen( 125 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY) 126 .setWindowingMode(WINDOWING_MODE_FULLSCREEN), 127 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY) 128 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 129 130 final SizeInfo dockedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 131 132 separateTestJournal(); 133 dismissSplitScreen(true /* primaryOnTop */); 134 final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 135 136 assertSizesAreSane(fullscreenSizes, dockedSizes); 137 } 138 139 /** 140 * Tests whether the Display sizes change when rotating the device. 141 */ 142 @Test testConfigurationUpdatesWhenRotatingWhileFullscreen()143 public void testConfigurationUpdatesWhenRotatingWhileFullscreen() { 144 assumeTrue("Skipping test: no rotation support", supportsRotation()); 145 146 final RotationSession rotationSession = createManagedRotationSession(); 147 rotationSession.set(ROTATION_0); 148 149 separateTestJournal(); 150 final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession(); 151 resizeableActivityClient.startActivity(getLaunchActivityBuilder() 152 .setUseInstrumentation() 153 .setTargetActivity(RESIZEABLE_ACTIVITY) 154 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 155 final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 156 157 rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes); 158 } 159 160 /** 161 * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity 162 * is in the docked stack. 163 */ 164 @Test testConfigurationUpdatesWhenRotatingWhileDocked()165 public void testConfigurationUpdatesWhenRotatingWhileDocked() { 166 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 167 168 final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession(); 169 final RotationSession rotationSession = createManagedRotationSession(); 170 rotationSession.set(ROTATION_0); 171 172 separateTestJournal(); 173 // Launch our own activity to side in case Recents (or other activity to side) doesn't 174 // support rotation. 175 launchActivitiesInSplitScreen( 176 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 177 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 178 // Launch target activity in docked stack. 179 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY) 180 .setActivitySessionClient(resizeableActivityClient).execute(); 181 final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 182 183 rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes); 184 } 185 186 /** 187 * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity 188 * is launched to side from docked stack. 189 */ 190 @Test testConfigurationUpdatesWhenRotatingToSideFromDocked()191 public void testConfigurationUpdatesWhenRotatingToSideFromDocked() { 192 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 193 194 final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession(); 195 final RotationSession rotationSession = createManagedRotationSession(); 196 rotationSession.set(ROTATION_0); 197 198 separateTestJournal(); 199 launchActivitiesInSplitScreen( 200 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 201 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY) 202 .setActivitySessionClient(resizeableActivityClient)); 203 final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 204 205 rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes); 206 } 207 rotateAndCheckSizes(RotationSession rotationSession, ActivitySessionClient noRelaunchActivityClient, SizeInfo prevSizes)208 private void rotateAndCheckSizes(RotationSession rotationSession, 209 ActivitySessionClient noRelaunchActivityClient, SizeInfo prevSizes) { 210 final ActivitySession activitySession = noRelaunchActivityClient.getLastStartedSession(); 211 final ComponentName activityName = activitySession.getName(); 212 final WindowManagerState.Task task = mWmState.getTaskByActivity(activityName); 213 final int displayId = mWmState.getRootTask(task.getRootTaskId()).mDisplayId; 214 215 assumeTrue(supportsLockedUserRotation(rotationSession, displayId)); 216 217 final boolean isCloseToSquareDisplay = isCloseToSquareDisplay(); 218 final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 }; 219 for (final int rotation : rotations) { 220 separateTestJournal(); 221 rotationSession.set(rotation); 222 final int newDeviceRotation = getDeviceRotation(displayId); 223 if (newDeviceRotation == INVALID_DEVICE_ROTATION) { 224 logE("Got an invalid device rotation value. " 225 + "Continuing the test despite of that, but it is likely to fail."); 226 } 227 final boolean expectConfigChange = task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN 228 && !isCloseToSquareDisplay; 229 if (expectConfigChange) { 230 assertActivityLifecycle(activityName, false /* relaunch */); 231 } 232 final SizeInfo rotatedSizes = activitySession.getConfigInfo().sizeInfo; 233 assertSizesRotate(prevSizes, rotatedSizes, 234 // Skip orientation checks if we are not in fullscreen mode, or when the display 235 // is close to square because the app config orientation may always be landscape 236 // excluding the system insets. 237 !expectConfigChange /* skipOrientationCheck */); 238 prevSizes = rotatedSizes; 239 } 240 } 241 242 /** 243 * Tests when activity moved from fullscreen stack to docked and back. Activity will be 244 * relaunched twice and it should have same config as initial one. 245 */ 246 @Test testSameConfigurationFullSplitFullRelaunch()247 public void testSameConfigurationFullSplitFullRelaunch() { 248 moveActivityFullSplitFull(true /* relaunch */); 249 } 250 251 /** 252 * Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch. 253 */ 254 @Test testSameConfigurationFullSplitFullNoRelaunch()255 public void testSameConfigurationFullSplitFullNoRelaunch() { 256 moveActivityFullSplitFull(false /* relaunch */); 257 } 258 259 /** 260 * Launches activity in fullscreen task, moves to docked task and back to fullscreen task. 261 * Asserts that initial and final reported sizes in fullscreen task are the same. 262 */ moveActivityFullSplitFull(boolean relaunch)263 private void moveActivityFullSplitFull(boolean relaunch) { 264 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 265 266 final ComponentName activityName = relaunch ? TEST_ACTIVITY : RESIZEABLE_ACTIVITY; 267 // Launch to fullscreen task and record size. 268 separateTestJournal(); 269 launchActivity(activityName, WINDOWING_MODE_FULLSCREEN); 270 final SizeInfo initialFullscreenSizes = getLastReportedSizesForActivity(activityName); 271 272 // Ensure the orientation configuration is different while moving the activity into split 273 // primary task later if we expected activity to be launched. 274 if (relaunch) { 275 mTaskOrganizer.registerOrganizerIfNeeded(); 276 Rect primaryTaskBounds = mTaskOrganizer.getPrimaryTaskBounds(); 277 if (initialFullscreenSizes.displayHeight > initialFullscreenSizes.displayWidth) { 278 primaryTaskBounds.bottom = primaryTaskBounds.width() / 2; 279 } else { 280 primaryTaskBounds.right = primaryTaskBounds.height() / 2; 281 } 282 mTaskOrganizer.setRootPrimaryTaskBounds(primaryTaskBounds); 283 } 284 285 // Move the task to the primary split task. 286 separateTestJournal(); 287 putActivityInPrimarySplit(activityName); 288 // Currently launchActivityInPrimarySplit launches the target activity and then move it 289 // to split task, so it requires waiting of lifecycle to get the stable initial size. 290 if (relaunch) { 291 assertActivityLifecycle(activityName, true /* relaunch */); 292 } else { 293 // The lifecycle callbacks contain the initial launch event so only wait for 294 // multi-window mode changed. 295 waitForOnMultiWindowModeChanged(activityName); 296 } 297 final SizeInfo dockedSizes = getLastReportedSizesForActivity(activityName); 298 assertSizesAreSane(initialFullscreenSizes, dockedSizes); 299 final boolean orientationChanged = 300 initialFullscreenSizes.orientation != dockedSizes.orientation; 301 302 separateTestJournal(); 303 // Restore to fullscreen. 304 final int activityTaskId = mWmState.getTaskByActivity(activityName).getTaskId(); 305 final WindowContainerTransaction wct = new WindowContainerTransaction() 306 .setWindowingMode(mTaskOrganizer.getTaskInfo(activityTaskId).getToken(), 307 WINDOWING_MODE_FULLSCREEN); 308 mTaskOrganizer.dismissSplitScreen(wct, false /* primaryOnTop */); 309 // Home task could be on top since it was the top-most task while in split-screen mode 310 // (dock task was minimized), start the activity again to ensure the activity is at 311 // foreground. 312 launchActivity(activityName, WINDOWING_MODE_FULLSCREEN); 313 if (relaunch && !orientationChanged) { 314 // If there is no orientation changes while moving the non-resizeable activity out of 315 // the split, the Activity won't be relaunched because size changes won't cross the 316 // size config buckets. So, there won't be any lifecycle changes. 317 waitForOnMultiWindowModeChanged(activityName); 318 } else { 319 assertActivityLifecycle(activityName, relaunch); 320 } 321 322 // It must report same size as original one after split-screen dismissed. 323 final SizeInfo finalFullscreenSizes = getLastReportedSizesForActivity(activityName); 324 assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes); 325 } 326 327 /** 328 * Tests that an activity with the DialogWhenLarge theme can transform properly when in split 329 * screen. 330 */ 331 @Test testDialogWhenLargeSplitSmall()332 public void testDialogWhenLargeSplitSmall() { 333 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 334 335 launchActivitiesInSplitScreen( 336 getLaunchActivityBuilder().setTargetActivity(DIALOG_WHEN_LARGE_ACTIVITY), 337 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 338 int displayId = mWmState.getDisplayByActivity(DIALOG_WHEN_LARGE_ACTIVITY); 339 final WindowManagerState.DisplayContent display = mWmState.getDisplay(displayId); 340 final int density = display.getDpi(); 341 final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density); 342 final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density); 343 344 mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, smallWidthPx, smallHeightPx)); 345 mWmState.waitForValidState( 346 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY) 347 .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW) 348 .setActivityType(ACTIVITY_TYPE_STANDARD) 349 .build()); 350 } 351 352 /** 353 * Test that device handles consequent requested orientations and displays the activities. 354 */ 355 @Test testFullscreenAppOrientationRequests()356 public void testFullscreenAppOrientationRequests() { 357 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 358 359 separateTestJournal(); 360 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY); 361 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 362 SizeInfo reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY); 363 assertEquals("portrait activity should be in portrait", 364 ORIENTATION_PORTRAIT, reportedSizes.orientation); 365 assertTrue("portrait activity should have height >= width", 366 reportedSizes.heightDp >= reportedSizes.widthDp); 367 separateTestJournal(); 368 369 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 370 mInstrumentation.getUiAutomation().syncInputTransactions(); 371 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 372 reportedSizes = getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 373 assertEquals("landscape activity should be in landscape", 374 ORIENTATION_LANDSCAPE, reportedSizes.orientation); 375 assertTrue("landscape activity should have height < width", 376 reportedSizes.heightDp < reportedSizes.widthDp); 377 separateTestJournal(); 378 379 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY); 380 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 381 reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY); 382 assertEquals("portrait activity should be in portrait", 383 ORIENTATION_PORTRAIT, reportedSizes.orientation); 384 } 385 386 @Test testTranslucentAppOrientationRequests()387 public void testTranslucentAppOrientationRequests() { 388 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 389 disableIgnoreOrientationRequest(); 390 391 separateTestJournal(); 392 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 393 final SizeInfo initialReportedSizes = 394 getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY); 395 assertEquals("portrait activity should be in portrait", 396 ORIENTATION_PORTRAIT, initialReportedSizes.orientation); 397 assertTrue("portrait activity should have height >= width", 398 initialReportedSizes.heightDp >= initialReportedSizes.widthDp); 399 assumeFalse("Skipping test: device is fixed to user rotation", 400 mWmState.isFixedToUserRotation()); 401 402 separateTestJournal(); 403 404 launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 405 assumeNotIgnoringOrientation(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY); 406 assertEquals("Legacy translucent activity requested landscape orientation", 407 SCREEN_ORIENTATION_LANDSCAPE, mWmState.getLastOrientation()); 408 409 // TODO(b/36897968): uncomment once we can suppress unsupported configurations 410 // final ReportedSizes updatedReportedSizes = 411 // getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator); 412 // assertEquals("portrait activity should not have moved from portrait", 413 // 1 /* portrait */, updatedReportedSizes.orientation); 414 } 415 416 /** 417 * Test that device handles consequent requested orientations and will not report a config 418 * change to an invisible activity. 419 */ 420 @Test testAppOrientationRequestConfigChanges()421 public void testAppOrientationRequestConfigChanges() { 422 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 423 424 separateTestJournal(); 425 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 426 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 427 428 assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, 429 1 /* create */, 1 /* start */, 1 /* resume */, 430 0 /* pause */, 0 /* stop */, 0 /* destroy */, 0 /* config */); 431 432 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 433 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 434 435 assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, 436 1 /* create */, 1 /* start */, 1 /* resume */, 437 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */); 438 assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY, 439 1 /* create */, 1 /* start */, 1 /* resume */, 440 0 /* pause */, 0 /* stop */, 0 /* destroy */, 0 /* config */); 441 442 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 443 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 444 445 assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, 446 2 /* create */, 2 /* start */, 2 /* resume */, 447 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */); 448 assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY, 449 1 /* create */, 1 /* start */, 1 /* resume */, 450 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */); 451 } 452 453 @Test testRotatedInfoWithFixedRotationTransform()454 public void testRotatedInfoWithFixedRotationTransform() { 455 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 456 457 // Start a portrait activity first to ensure that the orientation will change. 458 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY); 459 mWmState.waitForDisplayOrientation(ORIENTATION_PORTRAIT); 460 final int prevRotation = mWmState.getRotation(); 461 462 getLaunchActivityBuilder() 463 .setUseInstrumentation() 464 .setTargetActivity(LANDSCAPE_ORIENTATION_ACTIVITY) 465 // Request the info from onCreate because at that moment the real display hasn't 466 // rotated but the activity is rotated. 467 .setIntentExtra(bundle -> bundle.putBoolean(EXTRA_CONFIG_INFO_IN_ON_CREATE, true)) 468 .execute(); 469 mWmState.waitForDisplayOrientation(ORIENTATION_LANDSCAPE); 470 471 final SizeInfo reportedSizes = 472 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 473 final Bundle extras = TestJournalContainer.get(LANDSCAPE_ORIENTATION_ACTIVITY).extras; 474 final ConfigInfo appConfigInfo = extras.getParcelable(EXTRA_APP_CONFIG_INFO); 475 final Point onCreateRealDisplaySize = extras.getParcelable(EXTRA_DISPLAY_REAL_SIZE); 476 final ConfigInfo onCreateConfigInfo = extras.getParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE); 477 final SizeInfo onCreateSize = onCreateConfigInfo.sizeInfo; 478 final ConfigInfo globalConfigInfo = 479 extras.getParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO); 480 final SizeInfo globalSizeInfo = globalConfigInfo.sizeInfo; 481 482 assertEquals("The last reported size should be the same as the one from onCreate", 483 reportedSizes, onCreateConfigInfo.sizeInfo); 484 485 final WindowManagerState.DisplayContent dc = mWmState.getDisplay(Display.DEFAULT_DISPLAY); 486 final Point realDisplaySize = 487 new Point(dc.getDisplayRect().width(), dc.getDisplayRect().height()); 488 final int currentRotation = mWmState.getRotation(); 489 // Some devices may launch the activity in a letterboxed area so the display won't rotate. 490 final boolean displayRotationChanged = prevRotation != currentRotation; 491 492 assertEquals("The activity should get the final display rotation in onCreate", 493 currentRotation, onCreateConfigInfo.rotation); 494 assertEquals("The application should get the final display rotation in onCreate", 495 currentRotation, appConfigInfo.rotation); 496 assertEquals("The orientation of application must be landscape", 497 ORIENTATION_LANDSCAPE, appConfigInfo.sizeInfo.orientation); 498 assertEquals("The orientation of system resources must be landscape", 499 ORIENTATION_LANDSCAPE, globalSizeInfo.orientation); 500 501 final boolean isLandscape = onCreateSize.displayWidth > onCreateSize.displayHeight; 502 if (displayRotationChanged) { 503 assertEquals("The activity should get the final display size in onCreate", 504 realDisplaySize, onCreateRealDisplaySize); 505 assertEquals("The app size of activity should have the same orientation", isLandscape, 506 realDisplaySize.x > realDisplaySize.y); 507 assertEquals("The display metrics of system resources must be landscape", isLandscape, 508 globalSizeInfo.metricsWidth > globalSizeInfo.metricsHeight); 509 } 510 assertEquals("The application should get the same orientation", isLandscape, 511 appConfigInfo.sizeInfo.displayWidth > appConfigInfo.sizeInfo.displayHeight); 512 assertEquals("The app display metrics must be landscape", isLandscape, 513 appConfigInfo.sizeInfo.metricsWidth > appConfigInfo.sizeInfo.metricsHeight); 514 } 515 516 @Test testTranslucentActivityPermitted()517 public void testTranslucentActivityPermitted() throws Exception { 518 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 519 520 disableIgnoreOrientationRequest(); 521 522 final RotationSession rotationSession = createManagedRotationSession(); 523 rotationSession.set(ROTATION_0); 524 525 launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 526 assumeNotIgnoringOrientation(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY); 527 mWmState.assertResumedActivity( 528 "target SDK <= 26 translucent activity should be allowed to launch", 529 SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY); 530 assertEquals("translucent activity requested landscape orientation", 531 SCREEN_ORIENTATION_LANDSCAPE, mWmState.getLastOrientation()); 532 } 533 534 /** 535 * Test that device handles moving between two tasks with different orientations. 536 */ 537 @Test testTaskCloseRestoreFixedOrientation()538 public void testTaskCloseRestoreFixedOrientation() { 539 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 540 disableIgnoreOrientationRequest(); 541 542 // Start landscape activity. 543 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 544 assumeFalse("Skipping test: device is fixed to user rotation", 545 mWmState.isFixedToUserRotation()); 546 assumeNotIgnoringOrientation(LANDSCAPE_ORIENTATION_ACTIVITY); 547 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 548 mWmState.waitAndAssertLastOrientation("Fullscreen app requested landscape orientation", 549 SCREEN_ORIENTATION_LANDSCAPE); 550 551 // Start another activity in a different task. 552 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 553 554 // Request portrait 555 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT); 556 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT); 557 waitForBroadcastActivityReady(ORIENTATION_PORTRAIT); 558 559 // Finish activity 560 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 561 562 // Verify that activity brought to front is in originally requested orientation. 563 mWmState.computeState(LANDSCAPE_ORIENTATION_ACTIVITY); 564 mWmState.waitAndAssertLastOrientation("Should return to app in landscape orientation", 565 SCREEN_ORIENTATION_LANDSCAPE); 566 } 567 568 /** 569 * Test that device handles moving between two tasks with different orientations. 570 */ 571 @Test testTaskCloseRestoreFreeOrientation()572 public void testTaskCloseRestoreFreeOrientation() { 573 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 574 575 // Start landscape activity. 576 launchActivity(RESIZEABLE_ACTIVITY); 577 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 578 // This should be ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED (default value of 579 // android:screenOrientation). 580 final int initialServerOrientation = mWmState.getLastOrientation(); 581 582 // Verify fixed-landscape 583 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 584 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_LANDSCAPE); 585 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE); 586 waitForBroadcastActivityReady(ORIENTATION_LANDSCAPE); 587 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 588 589 // Verify that activity brought to front is in originally requested orientation. 590 mWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED); 591 // Note that the actual orientation of the activity can be either portrait or landscape 592 // depending on the sensor, so only verify the "requested orientation". 593 mWmState.waitAndAssertLastOrientation("Should come back in original server orientation", 594 initialServerOrientation); 595 596 // Verify fixed-portrait 597 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 598 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT); 599 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT); 600 waitForBroadcastActivityReady(ORIENTATION_PORTRAIT); 601 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 602 603 // Verify that activity brought to front is in originally requested orientation. 604 mWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED); 605 mWmState.waitAndAssertLastOrientation("Should come back in original server orientation", 606 initialServerOrientation); 607 } 608 609 /** 610 * Test that activity orientation will change when device is rotated. 611 * Also verify that occluded activity will not get config changes. 612 */ 613 @Test testAppOrientationWhenRotating()614 public void testAppOrientationWhenRotating() throws Exception { 615 assumeFalse("Skipping test: square size may not have configuration changes", 616 isCloseToSquareDisplay()); 617 assumeTrue("Skipping test: no rotation support", supportsRotation()); 618 619 // Start resizeable activity that handles configuration changes. 620 separateTestJournal(); 621 launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 622 launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 623 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 624 625 final int displayId = mWmState.getDisplayByActivity(RESIZEABLE_ACTIVITY); 626 627 // Rotate the activity and check that it receives configuration changes with a different 628 // orientation each time. 629 final RotationSession rotationSession = createManagedRotationSession(); 630 assumeTrue("Skipping test: no locked user rotation mode support.", 631 supportsLockedUserRotation(rotationSession, displayId)); 632 633 rotationSession.set(ROTATION_0); 634 SizeInfo reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 635 int prevOrientation = reportedSizes.orientation; 636 637 final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 }; 638 for (final int rotation : rotations) { 639 separateTestJournal(); 640 rotationSession.set(rotation); 641 642 // Verify lifecycle count and orientation changes. 643 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 644 1 /* numConfigChange */); 645 reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 646 assertNotEquals(prevOrientation, reportedSizes.orientation); 647 assertRelaunchOrConfigChanged(TEST_ACTIVITY, 0 /* numRelaunch */, 648 0 /* numConfigChange */); 649 650 prevOrientation = reportedSizes.orientation; 651 } 652 } 653 654 /** 655 * Test that the orientation for a simulated display context derived from an application context 656 * will not change when the device rotates. 657 */ 658 @Test testAppContextDerivedDisplayContextOrientationWhenRotating()659 public void testAppContextDerivedDisplayContextOrientationWhenRotating() { 660 assumeTrue("Skipping test: no rotation support", supportsRotation()); 661 assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay()); 662 663 assertDisplayContextDoesntChangeOrientationWhenRotating(Activity::getApplicationContext); 664 } 665 666 /** 667 * Test that the orientation for a simulated display context derived from an activity context 668 * will not change when the device rotates. 669 */ 670 @Test testActivityContextDerivedDisplayContextOrientationWhenRotating()671 public void testActivityContextDerivedDisplayContextOrientationWhenRotating() { 672 assumeTrue("Skipping test: no rotation support", supportsRotation()); 673 assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay()); 674 675 assertDisplayContextDoesntChangeOrientationWhenRotating(activity -> activity); 676 } 677 678 /** 679 * Asserts that the orientation for a simulated display context derived from a base context will 680 * not change when the device rotates. 681 * 682 * @param baseContextSupplier function that returns a base context used to created the display 683 * context. 684 * 685 * @see #testAppContextDerivedDisplayContextOrientationWhenRotating 686 * @see #testActivityContextDerivedDisplayContextOrientationWhenRotating 687 */ assertDisplayContextDoesntChangeOrientationWhenRotating( Function<Activity, Context> baseContextSupplier)688 private void assertDisplayContextDoesntChangeOrientationWhenRotating( 689 Function<Activity, Context> baseContextSupplier) { 690 RotationSession rotationSession = createManagedRotationSession(); 691 rotationSession.set(ROTATION_0); 692 693 TestActivitySession<ConfigChangeHandlingActivity> activitySession 694 = createManagedTestActivitySession(); 695 activitySession.launchTestActivityOnDisplaySync( 696 ConfigChangeHandlingActivity.class, 697 Display.DEFAULT_DISPLAY, 698 WINDOWING_MODE_FULLSCREEN); 699 final ConfigChangeHandlingActivity activity = activitySession.getActivity(); 700 701 VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession(); 702 WindowManagerState.DisplayContent displayContent = virtualDisplaySession 703 .setSimulateDisplay(true) 704 .setSimulationDisplaySize(100 /* width */, 200 /* height */) 705 .createDisplay(); 706 707 DisplayManager dm = activity.getSystemService(DisplayManager.class); 708 Display simulatedDisplay = dm.getDisplay(displayContent.mId); 709 Context simulatedDisplayContext = baseContextSupplier.apply(activity) 710 .createDisplayContext(simulatedDisplay); 711 assertEquals(ORIENTATION_PORTRAIT, 712 simulatedDisplayContext.getResources().getConfiguration().orientation); 713 714 separateTestJournal(); 715 716 final int[] rotations = {ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0}; 717 for (final int rotation : rotations) { 718 rotationSession.set(rotation); 719 720 assertRelaunchOrConfigChanged(activity.getComponentName(), 0 /* numRelaunch */, 721 1 /* numConfigChange */); 722 separateTestJournal(); 723 724 assertEquals("Display context orientation must not be changed", ORIENTATION_PORTRAIT, 725 simulatedDisplayContext.getResources().getConfiguration().orientation); 726 } 727 } 728 729 /** 730 * Test that activity orientation will not change when trying to rotate fixed-orientation 731 * activity. 732 * Also verify that occluded activity will not get config changes. 733 */ 734 @Test testFixedOrientationWhenRotating()735 public void testFixedOrientationWhenRotating() { 736 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 737 // TODO(b/110533226): Fix test on devices with display cutout 738 assumeFalse("Skipping test: display cutout present, can't predict exact lifecycle", 739 hasDisplayCutout()); 740 disableIgnoreOrientationRequest(); 741 742 // Start portrait-fixed activity 743 separateTestJournal(); 744 final ActivitySession activitySession = createManagedActivityClientSession() 745 .startActivity(getLaunchActivityBuilder() 746 .setUseInstrumentation() 747 .setWindowingMode(WINDOWING_MODE_FULLSCREEN) 748 .setTargetActivity(RESIZEABLE_ACTIVITY)); 749 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 750 751 final int displayId = mWmState.getDisplayByActivity(RESIZEABLE_ACTIVITY); 752 final ComponentName fixedOrientationActivity = 753 mWmState.getDisplay(displayId).getFullConfiguration().orientation 754 == ORIENTATION_LANDSCAPE 755 ? LANDSCAPE_ORIENTATION_ACTIVITY : PORTRAIT_ORIENTATION_ACTIVITY; 756 757 final RotationSession rotationSession = createManagedRotationSession(); 758 assumeTrue("Skipping test: no user locked rotation support.", 759 supportsLockedUserRotation(rotationSession, displayId)); 760 761 launchActivity(fixedOrientationActivity, WINDOWING_MODE_FULLSCREEN); 762 assumeNotIgnoringOrientation(fixedOrientationActivity); 763 mWmState.assertVisibility(fixedOrientationActivity, true /* visible */); 764 final SizeInfo initialSize = activitySession.getConfigInfo().sizeInfo; 765 766 // Rotate the display and check that the orientation doesn't change 767 final int[] rotations = { ROTATION_0, ROTATION_270, ROTATION_180, ROTATION_90 }; 768 for (final int rotation : rotations) { 769 separateTestJournal(); 770 rotationSession.set(rotation, false /* waitDeviceRotation */); 771 772 // Verify lifecycle count and orientation changes. 773 assertRelaunchOrConfigChanged(fixedOrientationActivity, 0 /* numRelaunch */, 774 0 /* numConfigChange */); 775 final SizeInfo currentSize = activitySession.getConfigInfo().sizeInfo; 776 assertEquals("Sizes must not be changed", initialSize, currentSize); 777 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 778 0 /* numConfigChange */); 779 } 780 } 781 782 /** 783 * Test that device handles moving between two tasks with different orientations. 784 */ 785 @Test testTaskMoveToBackOrientation()786 public void testTaskMoveToBackOrientation() { 787 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 788 disableIgnoreOrientationRequest(); 789 790 // Start landscape activity. 791 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 792 assumeNotIgnoringOrientation(LANDSCAPE_ORIENTATION_ACTIVITY); 793 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 794 final boolean isFixedToUserRotation = mWmState.isFixedToUserRotation(); 795 if (!isFixedToUserRotation) { 796 mWmState.waitAndAssertLastOrientation( 797 "Fullscreen app requested landscape orientation", 798 SCREEN_ORIENTATION_LANDSCAPE); 799 } else { 800 final SizeInfo reportedSizes = 801 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 802 assertEquals("landscape activity should be in landscape", 803 ORIENTATION_LANDSCAPE, reportedSizes.orientation); 804 assertTrue("landscape activity should have height < width", 805 reportedSizes.heightDp < reportedSizes.widthDp); 806 } 807 808 // Start another activity in a different task. 809 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 810 811 // Request portrait 812 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT); 813 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT); 814 waitForBroadcastActivityReady(ORIENTATION_PORTRAIT); 815 816 // Finish activity 817 mBroadcastActionTrigger.moveTopTaskToBack(); 818 819 // Verify that activity brought to front is in originally requested orientation. 820 mWmState.waitForValidState(LANDSCAPE_ORIENTATION_ACTIVITY); 821 if (!isFixedToUserRotation) { 822 mWmState.waitAndAssertLastOrientation( 823 "Fullscreen app requested landscape orientation", 824 SCREEN_ORIENTATION_LANDSCAPE); 825 } else { 826 final SizeInfo reportedSizes = 827 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 828 assertEquals("landscape activity should be in landscape", 829 ORIENTATION_LANDSCAPE, reportedSizes.orientation); 830 assertTrue("landscape activity should have height < width", 831 reportedSizes.heightDp < reportedSizes.widthDp); 832 } 833 } 834 835 /** 836 * Test that device doesn't change device orientation by app request while in multi-window. 837 */ 838 @Test testSplitscreenPortraitAppOrientationRequests()839 public void testSplitscreenPortraitAppOrientationRequests() throws Exception { 840 requestOrientationInSplitScreen(createManagedRotationSession(), 841 isDisplayPortrait() ? ROTATION_0 : ROTATION_90, LANDSCAPE_ORIENTATION_ACTIVITY); 842 } 843 844 /** 845 * Test that device doesn't change device orientation by app request while in multi-window. 846 */ 847 @Test testSplitscreenLandscapeAppOrientationRequests()848 public void testSplitscreenLandscapeAppOrientationRequests() throws Exception { 849 requestOrientationInSplitScreen(createManagedRotationSession(), 850 isDisplayPortrait() ? ROTATION_90 : ROTATION_0, PORTRAIT_ORIENTATION_ACTIVITY); 851 } 852 853 /** 854 * Launch specified activity in split-screen then rotate the divice, checking orientation 855 * should always change by the device rotation. 856 */ requestOrientationInSplitScreen(RotationSession rotationSession, int rotation, ComponentName activity)857 private void requestOrientationInSplitScreen(RotationSession rotationSession, int rotation, 858 ComponentName activity) throws Exception { 859 assumeTrue("Skipping test: no rotation support", supportsRotation()); 860 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 861 862 // Launch activity and move it to primary split-screen. 863 launchActivityInPrimarySplit(LAUNCHING_ACTIVITY); 864 865 // Launch target activity in secondary split-screen. 866 mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); 867 getLaunchActivityBuilder() 868 .setTargetActivity(activity) 869 .setUseInstrumentation() 870 .setWaitForLaunched(true) 871 .setNewTask(true) 872 .setMultipleTask(true) 873 .execute(); 874 mWmState.assertVisibility(activity, true /* visible */); 875 876 // Rotate the device and it should always rotate regardless orientation app requested. 877 rotationSession.set(rotation); 878 assertEquals("Split-screen apps shouldn't influence device orientation", 879 rotation, mWmState.getRotation()); 880 881 // Launch target activity during split-screen again and check orientation still not change. 882 mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); 883 getLaunchActivityBuilder().setMultipleTask(true).setTargetActivity(activity).execute(); 884 mWmState.computeState(activity); 885 mWmState.assertVisibility(activity, true /* visible */); 886 assertEquals("Split-screen apps shouldn't influence device orientation", 887 rotation, mWmState.getRotation()); 888 } 889 890 /** 891 * Asserts that after rotation, the aspect ratios of display size, metrics, and configuration 892 * have flipped. 893 */ assertSizesRotate(SizeInfo rotationA, SizeInfo rotationB, boolean skipOrientationCheck)894 private static void assertSizesRotate(SizeInfo rotationA, SizeInfo rotationB, 895 boolean skipOrientationCheck) { 896 assertEquals(rotationA.displayWidth, rotationA.metricsWidth); 897 assertEquals(rotationA.displayHeight, rotationA.metricsHeight); 898 assertEquals(rotationB.displayWidth, rotationB.metricsWidth); 899 assertEquals(rotationB.displayHeight, rotationB.metricsHeight); 900 901 if (skipOrientationCheck) { 902 // All done if we are not doing orientation check. 903 return; 904 } 905 final boolean beforePortrait = rotationA.displayWidth < rotationA.displayHeight; 906 final boolean afterPortrait = rotationB.displayWidth < rotationB.displayHeight; 907 assertFalse(beforePortrait == afterPortrait); 908 909 final boolean beforeConfigPortrait = rotationA.widthDp < rotationA.heightDp; 910 final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp; 911 assertEquals(beforePortrait, beforeConfigPortrait); 912 assertEquals(afterPortrait, afterConfigPortrait); 913 914 assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp); 915 } 916 917 /** 918 * Throws an AssertionError if fullscreenSizes has widths/heights (depending on aspect ratio) 919 * that are smaller than the dockedSizes. 920 */ 921 private static void assertSizesAreSane(SizeInfo fullscreenSizes, SizeInfo dockedSizes) { 922 final boolean isHorizontalDivision = 923 fullscreenSizes.displayHeight - dockedSizes.displayHeight > 924 fullscreenSizes.displayWidth - dockedSizes.displayWidth; 925 if (isHorizontalDivision) { 926 assertThat(dockedSizes.displayHeight, lessThan(fullscreenSizes.displayHeight)); 927 assertThat(dockedSizes.heightDp, lessThan(fullscreenSizes.heightDp)); 928 assertThat(dockedSizes.metricsHeight, lessThan(fullscreenSizes.metricsHeight)); 929 } else { 930 assertThat(dockedSizes.displayWidth, lessThan(fullscreenSizes.displayWidth)); 931 assertThat(dockedSizes.widthDp, lessThan(fullscreenSizes.widthDp)); 932 assertThat(dockedSizes.metricsWidth, lessThan(fullscreenSizes.metricsWidth)); 933 } 934 } 935 936 /** 937 * Throws an AssertionError if sizes are different. 938 */ 939 private static void assertSizesAreSame(SizeInfo firstSize, SizeInfo secondSize) { 940 assertEquals(firstSize.widthDp, secondSize.widthDp); 941 assertEquals(firstSize.heightDp, secondSize.heightDp); 942 assertEquals(firstSize.displayWidth, secondSize.displayWidth); 943 assertEquals(firstSize.displayHeight, secondSize.displayHeight); 944 assertEquals(firstSize.metricsWidth, secondSize.metricsWidth); 945 assertEquals(firstSize.metricsHeight, secondSize.metricsHeight); 946 assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp); 947 } 948 949 private void waitForBroadcastActivityReady(int orientation) { 950 mWmState.waitForActivityOrientation(BROADCAST_RECEIVER_ACTIVITY, orientation); 951 mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED); 952 } 953 954 /** 955 * Test launching an activity which requests specific UI mode during creation. 956 */ 957 @Test 958 public void testLaunchWithUiModeChange() { 959 // Launch activity that changes UI mode and handles this configuration change. 960 launchActivity(NIGHT_MODE_ACTIVITY); 961 mWmState.waitForActivityState(NIGHT_MODE_ACTIVITY, STATE_RESUMED); 962 963 // Check if activity is launched successfully. 964 mWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */); 965 mWmState.assertFocusedActivity("Launched activity should be focused", 966 NIGHT_MODE_ACTIVITY); 967 mWmState.assertResumedActivity("Launched activity must be resumed", NIGHT_MODE_ACTIVITY); 968 } 969 970 @Test 971 public void testAppConfigurationMatchesActivityInMultiWindow() throws Exception { 972 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 973 974 final ActivitySession activitySession = createManagedActivityClientSession() 975 .startActivity(getLaunchActivityBuilder() 976 .setUseInstrumentation() 977 .setTargetActivity(RESIZEABLE_ACTIVITY) 978 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 979 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 980 SizeInfo dockedActivitySizes = getActivitySizeInfo(activitySession); 981 SizeInfo applicationSizes = getAppSizeInfo(activitySession); 982 assertSizesAreSame(dockedActivitySizes, applicationSizes); 983 984 // Move the activity to fullscreen and check that the size was updated 985 separateTestJournal(); 986 mTaskOrganizer.dismissSplitScreen(true /* primaryOnTop */); 987 waitForOrFail("Activity and application configuration must match", 988 () -> activityAndAppSizesMatch(activitySession)); 989 final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 990 applicationSizes = getAppSizeInfo(activitySession); 991 assertSizesAreSane(fullscreenSizes, dockedActivitySizes); 992 assertSizesAreSame(fullscreenSizes, applicationSizes); 993 994 // Move the activity to docked size again, check if the sizes were updated 995 separateTestJournal(); 996 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 997 waitForOrFail("Activity and application configuration must match", 998 () -> activityAndAppSizesMatch(activitySession)); 999 dockedActivitySizes = getActivitySizeInfo(activitySession); 1000 applicationSizes = getAppSizeInfo(activitySession); 1001 assertSizesAreSane(fullscreenSizes, dockedActivitySizes); 1002 assertSizesAreSame(dockedActivitySizes, applicationSizes); 1003 } 1004 1005 @Test 1006 public void testAppConfigurationMatchesTopActivityInMultiWindow() throws Exception { 1007 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 1008 1009 // Launch initial activity in fullscreen and assert sizes 1010 final ActivitySession fullscreenActivitySession = createManagedActivityClientSession() 1011 .startActivity(getLaunchActivityBuilder() 1012 .setUseInstrumentation() 1013 .setTargetActivity(TEST_ACTIVITY) 1014 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 1015 SizeInfo fullscreenActivitySizes = getActivitySizeInfo(fullscreenActivitySession); 1016 SizeInfo applicationSizes = getAppSizeInfo(fullscreenActivitySession); 1017 assertSizesAreSame(fullscreenActivitySizes, applicationSizes); 1018 1019 // Launch second activity in split-screen and assert that sizes were updated 1020 separateTestJournal(); 1021 final ActivitySession secondActivitySession = createManagedActivityClientSession() 1022 .startActivity(getLaunchActivityBuilder() 1023 .setUseInstrumentation() 1024 .setTargetActivity(RESIZEABLE_ACTIVITY) 1025 .setNewTask(true) 1026 .setMultipleTask(true)); 1027 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 1028 waitForOrFail("Activity and application configuration must match", 1029 () -> activityAndAppSizesMatch(secondActivitySession)); 1030 SizeInfo dockedActivitySizes = getActivitySizeInfo(secondActivitySession); 1031 applicationSizes = getAppSizeInfo(secondActivitySession); 1032 assertSizesAreSame(dockedActivitySizes, applicationSizes); 1033 assertSizesAreSane(fullscreenActivitySizes, dockedActivitySizes); 1034 1035 // Launch third activity in secondary split-screen and assert that sizes were updated 1036 separateTestJournal(); 1037 final ActivitySession thirdActivitySession = createManagedActivityClientSession() 1038 .startActivity(getLaunchActivityBuilder() 1039 .setUseInstrumentation() 1040 .setTargetActivity(RESIZEABLE_ACTIVITY) 1041 .setNewTask(true) 1042 .setMultipleTask(true)); 1043 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 1044 waitForOrFail("Activity and application configuration must match", 1045 () -> activityAndAppSizesMatch(thirdActivitySession)); 1046 SizeInfo secondarySplitActivitySizes = getActivitySizeInfo(thirdActivitySession); 1047 applicationSizes = getAppSizeInfo(thirdActivitySession); 1048 assertSizesAreSame(secondarySplitActivitySizes, applicationSizes); 1049 assertSizesAreSane(fullscreenActivitySizes, secondarySplitActivitySizes); 1050 } 1051 1052 @Test testAppConfigurationMatchesActivityInFreeform()1053 public void testAppConfigurationMatchesActivityInFreeform() throws Exception { 1054 assumeTrue("Skipping test: no freeform support", supportsFreeform()); 1055 1056 // Launch activity in freeform and assert sizes 1057 final ActivitySession freeformActivitySession = createManagedActivityClientSession() 1058 .startActivity(getLaunchActivityBuilder() 1059 .setUseInstrumentation() 1060 .setTargetActivity(TEST_ACTIVITY) 1061 .setWindowingMode(WINDOWING_MODE_FREEFORM)); 1062 SizeInfo freeformActivitySizes = getActivitySizeInfo(freeformActivitySession); 1063 SizeInfo applicationSizes = getAppSizeInfo(freeformActivitySession); 1064 assertSizesAreSame(freeformActivitySizes, applicationSizes); 1065 } 1066 activityAndAppSizesMatch(ActivitySession activitySession)1067 private boolean activityAndAppSizesMatch(ActivitySession activitySession) { 1068 final SizeInfo activitySize = activitySession.getConfigInfo().sizeInfo; 1069 final SizeInfo appSize = activitySession.getAppConfigInfo().sizeInfo; 1070 return activitySize.equals(appSize); 1071 } 1072 getActivitySizeInfo(ActivitySession activitySession)1073 private SizeInfo getActivitySizeInfo(ActivitySession activitySession) { 1074 return activitySession.getConfigInfo().sizeInfo; 1075 } 1076 getAppSizeInfo(ActivitySession activitySession)1077 private SizeInfo getAppSizeInfo(ActivitySession activitySession) { 1078 return activitySession.getAppConfigInfo().sizeInfo; 1079 } 1080 assumeNotIgnoringOrientation(ComponentName activityName)1081 private void assumeNotIgnoringOrientation(ComponentName activityName) { 1082 assumeFalse("Skipping test: display area is ignoring orientation request", 1083 getWmState().isTaskDisplayAreaIgnoringOrientationRequest(activityName)); 1084 } 1085 } 1086