1 /* 2 * Copyright (C) 2021 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 android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 20 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; 21 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 22 import static android.server.wm.SplitActivityLifecycleTest.ActivityB.EXTRA_SHOW_WHEN_LOCKED; 23 import static android.server.wm.WindowManagerState.STATE_STARTED; 24 import static android.server.wm.WindowManagerState.STATE_STOPPED; 25 26 import static com.google.common.truth.Truth.assertThat; 27 import static com.google.common.truth.Truth.assertWithMessage; 28 29 import static org.junit.Assume.assumeTrue; 30 31 import android.app.Activity; 32 import android.content.ComponentName; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.graphics.Rect; 36 import android.os.Bundle; 37 import android.os.IBinder; 38 import android.platform.test.annotations.Presubmit; 39 import android.server.wm.WindowManagerState.Task; 40 import android.server.wm.WindowManagerState.TaskFragment; 41 import android.window.TaskFragmentCreationParams; 42 import android.window.TaskFragmentInfo; 43 import android.window.WindowContainerToken; 44 import android.window.WindowContainerTransaction; 45 46 import org.junit.Ignore; 47 import org.junit.Test; 48 49 /** 50 * Tests that verify the behavior of split Activity. 51 * <p> 52 * At the beginning of test, two Activities are launched side-by-side in two adjacent TaskFragments. 53 * Then another Activity will be launched with different scenarios. The purpose of this test is to 54 * verify the CUJ of split Activity. 55 * </p> 56 * 57 * Build/Install/Run: 58 * atest CtsWindowManagerDeviceTestCases:SplitActivityLifecycleTest 59 */ 60 @Presubmit 61 public class SplitActivityLifecycleTest extends TaskFragmentOrganizerTestBase { 62 private Activity mOwnerActivity; 63 private IBinder mOwnerToken; 64 private final Rect mPrimaryBounds = new Rect(); 65 private final Rect mSideBounds = new Rect(); 66 private TaskFragmentRecord mTaskFragA; 67 private TaskFragmentRecord mTaskFragB; 68 private final ComponentName mActivityA = new ComponentName(mContext, ActivityA.class); 69 private final ComponentName mActivityB = new ComponentName(mContext, ActivityB.class); 70 private final ComponentName mActivityC = new ComponentName(mContext, ActivityC.class); 71 private final Intent mIntent = new Intent().setComponent(mActivityC); 72 73 @Override setUp()74 public void setUp() throws Exception { 75 super.setUp(); 76 mOwnerActivity = startActivity(ActivityA.class); 77 mOwnerToken = getActivityToken(mOwnerActivity); 78 } 79 80 /** Launch two Activities in two adjacent TaskFragments side-by-side. */ initializeSplitActivities(boolean splitInEmbeddedTask)81 private void initializeSplitActivities(boolean splitInEmbeddedTask) { 82 initializeSplitActivities(splitInEmbeddedTask, false /* showWhenLocked */); 83 } 84 85 /** 86 * Launch two Activities in two adjacent TaskFragments side-by-side and support to set the 87 * showWhenLocked attribute to Activity B. 88 */ initializeSplitActivities(boolean splitInEmbeddedTask, boolean showWhenLocked)89 private void initializeSplitActivities(boolean splitInEmbeddedTask, boolean showWhenLocked) { 90 final Rect activityBounds = mOwnerActivity.getWindowManager().getCurrentWindowMetrics() 91 .getBounds(); 92 activityBounds.splitVertically(mPrimaryBounds, mSideBounds); 93 94 final TaskFragmentCreationParams paramsA = generatePrimaryTaskFragParams(); 95 final TaskFragmentCreationParams paramsB = generateSideTaskFragParams(); 96 IBinder taskFragTokenA = paramsA.getFragmentToken(); 97 IBinder taskFragTokenB = paramsB.getFragmentToken(); 98 99 final WindowContainerTransaction wct = new WindowContainerTransaction() 100 .createTaskFragment(paramsA) 101 .reparentActivityToTaskFragment(taskFragTokenA, mOwnerToken) 102 .createTaskFragment(paramsB) 103 .setAdjacentTaskFragments(taskFragTokenA, taskFragTokenB, null /* params */); 104 105 final Intent intent = new Intent().setComponent(mActivityB); 106 if (splitInEmbeddedTask) { 107 intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK); 108 } 109 if (showWhenLocked) { 110 intent.putExtra(EXTRA_SHOW_WHEN_LOCKED, true); 111 } 112 wct.startActivityInTaskFragment(taskFragTokenB, mOwnerToken, intent, 113 null /* activityOptions */); 114 115 mTaskFragmentOrganizer.setAppearedCount(2); 116 mTaskFragmentOrganizer.applyTransaction(wct); 117 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 118 119 final TaskFragmentInfo infoA = mTaskFragmentOrganizer.getTaskFragmentInfo( 120 taskFragTokenA); 121 final TaskFragmentInfo infoB = mTaskFragmentOrganizer.getTaskFragmentInfo( 122 taskFragTokenB); 123 124 assertNotEmptyTaskFragment(infoA, taskFragTokenA, mOwnerToken); 125 assertNotEmptyTaskFragment(infoB, taskFragTokenB); 126 127 mTaskFragA = new TaskFragmentRecord(infoA); 128 mTaskFragB = new TaskFragmentRecord(infoB); 129 130 waitAndAssertResumedActivity(mActivityA, "Activity A must still be resumed."); 131 waitAndAssertResumedActivity(mActivityB, "Activity B must still be resumed."); 132 133 if (splitInEmbeddedTask) { 134 TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB); 135 Task embeddedTask = mWmState.getTaskByActivity(mActivityB); 136 assertWindowHierarchy(taskFragmentB, embeddedTask, mWmState.getActivity(mActivityB)); 137 } 138 139 mTaskFragmentOrganizer.resetLatch(); 140 } 141 142 /** 143 * Verifies the behavior to launch Activity in the same TaskFragment as the owner Activity. 144 * <p> 145 * For example, given that Activity A and B are showed side-by-side, this test verifies 146 * the behavior to launch Activity C in the same TaskFragment as Activity A: 147 * <pre class="prettyprint"> 148 * |A|B| -> |C|B| 149 * </pre></p> 150 */ 151 @Test testActivityLaunchInSameSplitTaskFragment()152 public void testActivityLaunchInSameSplitTaskFragment() { 153 // Initialize test environment by launching Activity A and B side-by-side. 154 initializeSplitActivities(false /* verifyEmbeddedTask */); 155 156 final IBinder taskFragTokenA = mTaskFragA.getTaskFragToken(); 157 final WindowContainerTransaction wct = new WindowContainerTransaction() 158 .startActivityInTaskFragment(taskFragTokenA, mOwnerToken, mIntent, 159 null /* activityOptions */); 160 161 mTaskFragmentOrganizer.applyTransaction(wct); 162 163 final TaskFragmentInfo infoA = mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo( 164 taskFragTokenA, info -> info.getActivities().size() == 2, 165 "getActivities from TaskFragment A must contain 2 activities"); 166 167 assertNotEmptyTaskFragment(infoA, taskFragTokenA, mOwnerToken); 168 169 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 170 waitAndAssertActivityState(mActivityA, STATE_STOPPED, 171 "Activity A is occluded by Activity C, so it must be stopped."); 172 waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed."); 173 174 final TaskFragment taskFragmentA = mWmState.getTaskFragmentByActivity(mActivityA); 175 assertWithMessage("TaskFragmentA must contain Activity A and C") 176 .that(taskFragmentA.mActivities).containsExactly(mWmState.getActivity(mActivityA), 177 mWmState.getActivity(mActivityC)); 178 } 179 180 /** 181 * Verifies the behavior to launch Activity in the adjacent TaskFragment. 182 * <p> 183 * For example, given that Activity A and B are showed side-by-side, this test verifies 184 * the behavior to launch Activity C in the same TaskFragment as Activity B: 185 * <pre class="prettyprint"> 186 * |A|B| -> |A|C| 187 * </pre></p> 188 */ 189 @Test testActivityLaunchInAdjacentSplitTaskFragment()190 public void testActivityLaunchInAdjacentSplitTaskFragment() { 191 // Initialize test environment by launching Activity A and B side-by-side. 192 initializeSplitActivities(false /* verifyEmbeddedTask */); 193 194 final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken(); 195 final WindowContainerTransaction wct = new WindowContainerTransaction() 196 .startActivityInTaskFragment(taskFragTokenB, mOwnerToken, mIntent, 197 null /* activityOptions */); 198 199 mTaskFragmentOrganizer.applyTransaction(wct); 200 201 final TaskFragmentInfo infoB = mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo( 202 taskFragTokenB, info -> info.getActivities().size() == 2, 203 "getActivities from TaskFragment A must contain 2 activities"); 204 205 assertNotEmptyTaskFragment(infoB, taskFragTokenB); 206 207 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 208 waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed."); 209 waitAndAssertActivityState(mActivityB, STATE_STOPPED, 210 "Activity B is occluded by Activity C, so it must be stopped."); 211 212 final TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB); 213 assertWithMessage("TaskFragmentB must contain Activity B and C") 214 .that(taskFragmentB.mActivities).containsExactly(mWmState.getActivity(mActivityB), 215 mWmState.getActivity(mActivityC)); 216 } 217 218 /** 219 * Verifies the behavior that the Activity instance in bottom TaskFragment calls 220 * {@link Context#startActivity(Intent)} to launch another Activity. 221 * <p> 222 * For example, given that Activity A and B are showed side-by-side, Activity A calls 223 * {@link Context#startActivity(Intent)} to launch Activity C. The expected behavior is that 224 * Activity C will be launch on top of Activity B as below: 225 * <pre class="prettyprint"> 226 * |A|B| -> |A|C| 227 * </pre> 228 * The reason is that TaskFragment B has higher z-order than TaskFragment A because we create 229 * TaskFragment B later than TaskFragment A. 230 * </p> 231 */ 232 @Test testActivityLaunchFromBottomTaskFragment()233 public void testActivityLaunchFromBottomTaskFragment() { 234 // Initialize test environment by launching Activity A and B side-by-side. 235 initializeSplitActivities(false /* verifyEmbeddedTask */); 236 237 mOwnerActivity.startActivity(mIntent); 238 239 final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken(); 240 final TaskFragmentInfo infoB = mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo( 241 taskFragTokenB, info -> info.getActivities().size() == 2, 242 "getActivities from TaskFragment A must contain 2 activities"); 243 244 assertNotEmptyTaskFragment(infoB, taskFragTokenB); 245 246 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 247 waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed."); 248 waitAndAssertActivityState(mActivityB, STATE_STOPPED, 249 "Activity B is occluded by Activity C, so it must be stopped."); 250 251 final TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB); 252 assertWithMessage("TaskFragmentB must contain Activity B and C") 253 .that(taskFragmentB.mActivities).containsExactly(mWmState.getActivity(mActivityB), 254 mWmState.getActivity(mActivityC)); 255 } 256 257 /** 258 * Verifies the behavior of the activities in a TaskFragment that is sandwiched in adjacent 259 * TaskFragments. 260 */ 261 @Test testSandwichTaskFragmentInAdjacent()262 public void testSandwichTaskFragmentInAdjacent() { 263 // Initialize test environment by launching Activity A and B side-by-side. 264 initializeSplitActivities(false /* verifyEmbeddedTask */); 265 266 final IBinder taskFragTokenA = mTaskFragA.getTaskFragToken(); 267 final TaskFragmentCreationParams paramsC = generateSideTaskFragParams(); 268 final IBinder taskFragTokenC = paramsC.getFragmentToken(); 269 final WindowContainerTransaction wct = new WindowContainerTransaction() 270 // Create the side TaskFragment for C and launch 271 .createTaskFragment(paramsC) 272 .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent, 273 null /* activityOptions */) 274 .setAdjacentTaskFragments(taskFragTokenA, taskFragTokenC, null /* options */); 275 276 mTaskFragmentOrganizer.applyTransaction(wct); 277 // Wait for the TaskFragment of Activity C to be created. 278 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 279 280 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 281 waitAndAssertActivityState(mActivityB, STATE_STOPPED, 282 "Activity B is occluded by Activity C, so it must be stopped."); 283 waitAndAssertResumedActivity(mActivityA, "Activity B must be resumed."); 284 } 285 286 /** 287 * Verifies the behavior to launch adjacent Activity to the adjacent TaskFragment. 288 * <p> 289 * For example, given that Activity A and B are showed side-by-side, this test verifies 290 * the behavior to launch the Activity C to the adjacent TaskFragment of the secondary 291 * TaskFragment, which Activity B is attached to. Then the secondary TaskFragment is shifted to 292 * occlude the primary TaskFragment, which Activity A is attached to, and the adjacent 293 * TaskFragment, which Activity C is attached to, is occupied the region where the secondary 294 * TaskFragment is located. This test is to verify the "shopping mode" scenario. 295 * <pre class="prettyprint"> 296 * |A|B| -> |B|C| 297 * </pre></p> 298 */ 299 @Test testAdjacentActivityLaunchFromSecondarySplitTaskFragment()300 public void testAdjacentActivityLaunchFromSecondarySplitTaskFragment() { 301 // Initialize test environment by launching Activity A and B side-by-side. 302 initializeSplitActivities(false /* verifyEmbeddedTask */); 303 304 final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken(); 305 final TaskFragmentCreationParams paramsC = generateSideTaskFragParams(); 306 final IBinder taskFragTokenC = paramsC.getFragmentToken(); 307 final WindowContainerTransaction wct = new WindowContainerTransaction() 308 // Move TaskFragment B to the primaryBounds 309 .setBounds(mTaskFragB.getToken(), mPrimaryBounds) 310 // Create the side TaskFragment for C and launch 311 .createTaskFragment(paramsC) 312 .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent, 313 null /* activityOptions */) 314 .setAdjacentTaskFragments(taskFragTokenB, taskFragTokenC, null /* options */); 315 316 mTaskFragmentOrganizer.applyTransaction(wct); 317 // Wait for the TaskFragment of Activity C to be created. 318 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 319 // Wait for the TaskFragment of Activity B to be changed. 320 mTaskFragmentOrganizer.waitForTaskFragmentInfoChanged(); 321 322 final TaskFragmentInfo infoB = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenB); 323 final TaskFragmentInfo infoC = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenC); 324 325 assertNotEmptyTaskFragment(infoB, taskFragTokenB); 326 assertNotEmptyTaskFragment(infoC, taskFragTokenC); 327 328 mTaskFragB = new TaskFragmentRecord(infoB); 329 final TaskFragmentRecord taskFragC = new TaskFragmentRecord(infoC); 330 331 assertThat(mTaskFragB.getBounds()).isEqualTo(mPrimaryBounds); 332 assertThat(taskFragC.getBounds()).isEqualTo(mSideBounds); 333 334 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 335 waitAndAssertActivityState(mActivityA, STATE_STOPPED, 336 "Activity A is occluded by Activity C, so it must be stopped."); 337 waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed."); 338 } 339 340 /** 341 * Verifies the behavior to launch Activity in expanded TaskFragment. 342 * <p> 343 * For example, given that Activity A and B are showed side-by-side, this test verifies 344 * the behavior to launch Activity C in the TaskFragment which fills the Task bounds of owner 345 * Activity: 346 * <pre class="prettyprint"> 347 * |A|B| -> |C| 348 * </pre></p> 349 */ 350 @Test testActivityLaunchInExpandedTaskFragment()351 public void testActivityLaunchInExpandedTaskFragment() { 352 // Initialize test environment by launching Activity A and B side-by-side. 353 initializeSplitActivities(false /* verifyEmbeddedTask */); 354 355 testActivityLaunchInExpandedTaskFragmentInternal(); 356 } 357 358 /** 359 * Verifies the behavior to launch Activity in expanded TaskFragment and occludes the embedded 360 * Task. 361 * <p> 362 * For example, given that Activity A and B are showed side-by-side, which Activity B is in 363 * embedded Task, this test verifies the behavior to launch Activity C in the TaskFragment which 364 * fills the Task bounds of owner Activity: 365 * <pre class="prettyprint"> 366 * - Fullscreen - 367 * TaskFragmentC 368 * - ActivityC <---- new started Activity 369 * - Left - - Right - 370 * TaskFragmentA TaskFragmentB 371 * - ActivityA - Embedded Task 372 * - ActivityB 373 * </pre></p> 374 */ 375 @Test 376 @Ignore("b/197364677") testActivityLaunchInExpandedTaskFragment_AboveEmbeddedTask()377 public void testActivityLaunchInExpandedTaskFragment_AboveEmbeddedTask() { 378 // Initialize test environment by launching Activity A and B side-by-side. 379 initializeSplitActivities(true /* verifyEmbeddedTask */); 380 381 testActivityLaunchInExpandedTaskFragmentInternal(); 382 } 383 testActivityLaunchInExpandedTaskFragmentInternal()384 private void testActivityLaunchInExpandedTaskFragmentInternal() { 385 386 final TaskFragmentCreationParams fullScreenParamsC = mTaskFragmentOrganizer 387 .generateTaskFragParams(mOwnerToken); 388 final IBinder taskFragTokenC = fullScreenParamsC.getFragmentToken(); 389 final WindowContainerTransaction wct = new WindowContainerTransaction() 390 .createTaskFragment(fullScreenParamsC) 391 .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent, 392 null /* activityOptions */); 393 394 mTaskFragmentOrganizer.applyTransaction(wct); 395 396 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 397 398 assertNotEmptyTaskFragment(mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenC), 399 taskFragTokenC); 400 401 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 402 waitAndAssertActivityState(mActivityA, STATE_STOPPED, 403 "Activity A is occluded by Activity C, so it must be stopped."); 404 waitAndAssertActivityState(mActivityB, STATE_STOPPED, 405 "Activity B is occluded by Activity C, so it must be stopped."); 406 } 407 408 /** 409 * Verifies the behavior to launch Activity above the embedded Task in TaskFragment. 410 * <p> 411 * For example, given that Activity A and B are showed side-by-side, which Activity B is in 412 * embedded Task, this test verifies the behavior to launch Activity C on top of the embedded 413 * Task in the same TaskFragment as Activity B: 414 * <pre class="prettyprint"> 415 * - Left - - Right - 416 * TaskFragmentA TaskFragmentB 417 * - ActivityA - ActivityC <---- new started Activity 418 * - Embedded Task 419 * - ActivityB 420 * </pre></p> 421 */ 422 @Test 423 @Ignore("b/197364677") testActivityLaunchAboveEmbeddedTaskInTaskFragment()424 public void testActivityLaunchAboveEmbeddedTaskInTaskFragment() { 425 // Initialize test environment by launching Activity A and B side-by-side. 426 initializeSplitActivities(true /* verifyEmbeddedTask */); 427 428 final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken(); 429 430 WindowContainerTransaction wct = new WindowContainerTransaction() 431 .startActivityInTaskFragment(taskFragTokenB, mOwnerToken, mIntent, 432 null /* activityOptions */); 433 434 mTaskFragmentOrganizer.applyTransaction(wct); 435 436 mTaskFragmentOrganizer.waitForTaskFragmentInfoChanged(); 437 438 final TaskFragmentInfo infoB = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragTokenB); 439 440 assertNotEmptyTaskFragment(infoB, taskFragTokenB); 441 442 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 443 waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed."); 444 waitAndAssertActivityState(mActivityB, WindowManagerState.STATE_STOPPED, 445 "Activity B is occluded by Activity C, so it must be stopped."); 446 447 final TaskFragment taskFragmentB = mWmState.getTaskFragmentByActivity(mActivityB); 448 assertWithMessage("TaskFragmentB must contain Activity C") 449 .that(taskFragmentB.mActivities).containsExactly(mWmState.getActivity(mActivityC)); 450 } 451 452 /** 453 * Verifies the behavior to launch Activity to the embedded Task in TaskFragment. 454 * <p> 455 * For example, given that Activity A and B are showed side-by-side, which Activity B is in 456 * embedded Task, this test verifies the behavior to launch Activity C to the embedded Task 457 * and on top of Activity B: 458 * <pre class="prettyprint"> 459 * - Left - - Right - 460 * TaskFragmentA TaskFragmentB 461 * - ActivityA - Embedded Task 462 * - ActivityC <---- new started Activity 463 * - ActivityB 464 * </pre></p> 465 */ 466 @Test 467 @Ignore("b/197364677") testActivityLaunchToEmbeddedTaskInTaskFragment()468 public void testActivityLaunchToEmbeddedTaskInTaskFragment() { 469 // Initialize test environment by launching Activity A and B side-by-side. 470 initializeSplitActivities(true /* verifyEmbeddedTask */); 471 472 final IBinder taskFragTokenB = mTaskFragB.getTaskFragToken(); 473 // Make Activity C launch to the embedded Task. 474 final Intent intent = new Intent(mIntent).addFlags(FLAG_ACTIVITY_NEW_TASK); 475 476 WindowContainerTransaction wct = new WindowContainerTransaction() 477 .startActivityInTaskFragment(taskFragTokenB, mOwnerToken, intent, 478 null /* activityOptions */); 479 480 mTaskFragmentOrganizer.applyTransaction(wct); 481 482 waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed."); 483 waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed."); 484 waitAndAssertActivityState(mActivityB, STATE_STOPPED, 485 "Activity B is occluded by Activity C, so it must be stopped."); 486 487 final Task embeddedTask = mWmState.getTaskByActivity(mActivityB); 488 assertWithMessage("Embedded Task must contain Activity B and Activity C") 489 .that(embeddedTask.mActivities).containsExactly(mWmState.getActivity(mActivityB), 490 mWmState.getActivity(mActivityC)); 491 } 492 493 /** 494 * Verifies the show-when-locked behavior while launch embedded activities. Don't show the 495 * embedded activities even if one of Activity has showWhenLocked flag. 496 */ 497 @Test testLaunchEmbeddedActivityWithShowWhenLocked()498 public void testLaunchEmbeddedActivityWithShowWhenLocked() { 499 assumeTrue(supportsLockScreen()); 500 501 final LockScreenSession lockScreenSession = createManagedLockScreenSession(); 502 // Initialize test environment by launching Activity A and B (with showWhenLocked) 503 // side-by-side. 504 initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */); 505 506 lockScreenSession.sleepDevice(); 507 lockScreenSession.wakeUpDevice(); 508 509 waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped"); 510 waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped"); 511 } 512 513 /** 514 * Verifies the show-when-locked behavior while launch embedded activities. Don't show the 515 * embedded activities if the activities don't have showWhenLocked flag. 516 */ 517 @Test testLaunchEmbeddedActivitiesWithoutShowWhenLocked()518 public void testLaunchEmbeddedActivitiesWithoutShowWhenLocked() { 519 assumeTrue(supportsLockScreen()); 520 521 final LockScreenSession lockScreenSession = createManagedLockScreenSession(); 522 // Initialize test environment by launching Activity A and B side-by-side. 523 initializeSplitActivities(false /* verifyEmbeddedTask */, false /* showWhenLocked */); 524 525 lockScreenSession.sleepDevice(); 526 lockScreenSession.wakeUpDevice(); 527 528 waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped"); 529 waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped"); 530 } 531 532 /** 533 * Verifies the show-when-locked behavior while launch embedded activities. The embedded 534 * activities should be shown on top of the lock screen since they have the showWhenLocked flag. 535 * Don't show the embedded activities even if one of Activity has showWhenLocked flag. 536 */ 537 @Test testLaunchEmbeddedActivitiesWithShowWhenLocked()538 public void testLaunchEmbeddedActivitiesWithShowWhenLocked() { 539 assumeTrue(supportsLockScreen()); 540 541 final LockScreenSession lockScreenSession = createManagedLockScreenSession(); 542 // Initialize test environment by launching Activity A and B side-by-side. 543 mOwnerActivity.setShowWhenLocked(true); 544 initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */); 545 546 lockScreenSession.sleepDevice(); 547 lockScreenSession.wakeUpDevice(); 548 549 waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed."); 550 waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed."); 551 552 // Launch Activity C without show-when-lock and verifies that both activities are stopped. 553 mOwnerActivity.startActivity(mIntent); 554 waitAndAssertActivityState(mActivityA, STATE_STOPPED, "Activity A must be stopped"); 555 waitAndAssertActivityState(mActivityC, STATE_STOPPED, "Activity C must be stopped"); 556 } 557 558 /** 559 * Verifies an Activity below adjacent translucent TaskFragments is visible. 560 */ 561 @Test testTranslucentAdjacentTaskFragment()562 public void testTranslucentAdjacentTaskFragment() { 563 // Create ActivityB on top of ActivityA 564 Activity activityB = startActivity(ActivityB.class); 565 waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed."); 566 waitAndAssertActivityState(mActivityA, STATE_STOPPED, 567 "Activity A is occluded by Activity B, so it must be stopped."); 568 569 // Create two adjacent TaskFragments, making ActivityB and TranslucentActivity 570 // displayed side-by-side (ActivityB|TranslucentActivity). 571 mOwnerActivity.getWindowManager().getCurrentWindowMetrics().getBounds() 572 .splitVertically(mPrimaryBounds, mSideBounds); 573 final TaskFragmentCreationParams primaryParams = generatePrimaryTaskFragParams(); 574 final TaskFragmentCreationParams secondaryParams = generateSideTaskFragParams(); 575 IBinder primaryToken = primaryParams.getFragmentToken(); 576 IBinder secondaryToken = secondaryParams.getFragmentToken(); 577 578 final ComponentName translucentActivity = new ComponentName(mContext, 579 TranslucentActivity.class); 580 final Intent intent = new Intent().setComponent(translucentActivity); 581 WindowContainerTransaction wct = new WindowContainerTransaction() 582 .createTaskFragment(primaryParams) 583 .reparentActivityToTaskFragment(primaryToken, getActivityToken(activityB)) 584 .createTaskFragment(secondaryParams) 585 .setAdjacentTaskFragments(primaryToken, secondaryToken, null /* params */) 586 .startActivityInTaskFragment(secondaryToken, mOwnerToken, intent, 587 null /* activityOptions */); 588 mTaskFragmentOrganizer.applyTransaction(wct); 589 590 waitAndAssertResumedActivity(translucentActivity, "TranslucentActivity must be resumed."); 591 waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed."); 592 waitAndAssertActivityState(mActivityA, STATE_STARTED, 593 "Activity A is not fully occluded and must be visible and started"); 594 } 595 generatePrimaryTaskFragParams()596 private TaskFragmentCreationParams generatePrimaryTaskFragParams() { 597 return mTaskFragmentOrganizer.generateTaskFragParams(mOwnerToken, mPrimaryBounds, 598 WINDOWING_MODE_MULTI_WINDOW); 599 } 600 generateSideTaskFragParams()601 private TaskFragmentCreationParams generateSideTaskFragParams() { 602 return mTaskFragmentOrganizer.generateTaskFragParams(mOwnerToken, mSideBounds, 603 WINDOWING_MODE_MULTI_WINDOW); 604 } 605 606 private static class TaskFragmentRecord { 607 private final IBinder mTaskFragToken; 608 private final Rect mBounds = new Rect(); 609 private final WindowContainerToken mContainerToken; 610 TaskFragmentRecord(TaskFragmentInfo info)611 private TaskFragmentRecord(TaskFragmentInfo info) { 612 mTaskFragToken = info.getFragmentToken(); 613 mBounds.set(info.getConfiguration().windowConfiguration.getBounds()); 614 mContainerToken = info.getToken(); 615 } 616 getTaskFragToken()617 private IBinder getTaskFragToken() { 618 return mTaskFragToken; 619 } 620 getBounds()621 private Rect getBounds() { 622 return mBounds; 623 } 624 getToken()625 private WindowContainerToken getToken() { 626 return mContainerToken; 627 } 628 } 629 630 public static class ActivityA extends SplitTestActivity {} 631 public static class ActivityB extends SplitTestActivity {} 632 public static class ActivityC extends SplitTestActivity {} 633 public static class TranslucentActivity extends SplitTestActivity {} 634 public static class SplitTestActivity extends FocusableActivity { 635 public static final String EXTRA_SHOW_WHEN_LOCKED = "showWhenLocked"; 636 @Override onCreate(Bundle icicle)637 protected void onCreate(Bundle icicle) { 638 super.onCreate(icicle); 639 if (getIntent().getBooleanExtra(EXTRA_SHOW_WHEN_LOCKED, false)) { 640 setShowWhenLocked(true); 641 } 642 } 643 } 644 } 645