1 /* 2 * Copyright (C) 2018 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.quickstep; 18 19 import static com.android.launcher3.Flags.enableLauncherOverviewInWindow; 20 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; 21 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; 22 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertFalse; 26 import static org.junit.Assert.assertNotEquals; 27 import static org.junit.Assert.assertNotNull; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assume.assumeFalse; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.content.Intent; 33 import android.content.res.Configuration; 34 35 import androidx.annotation.NonNull; 36 import androidx.test.filters.LargeTest; 37 import androidx.test.platform.app.InstrumentationRegistry; 38 import androidx.test.runner.AndroidJUnit4; 39 import androidx.test.uiautomator.By; 40 import androidx.test.uiautomator.Until; 41 42 import com.android.launcher3.LauncherState; 43 import com.android.launcher3.tapl.BaseOverview; 44 import com.android.launcher3.tapl.LaunchedAppState; 45 import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; 46 import com.android.launcher3.tapl.Overview; 47 import com.android.launcher3.tapl.OverviewActions; 48 import com.android.launcher3.tapl.OverviewTask; 49 import com.android.launcher3.tapl.SelectModeButtons; 50 import com.android.launcher3.tapl.Workspace; 51 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; 52 import com.android.launcher3.util.TestUtil; 53 import com.android.launcher3.util.Wait; 54 import com.android.launcher3.util.rule.ScreenRecordRule; 55 import com.android.launcher3.util.rule.TestStabilityRule; 56 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; 57 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch; 58 import com.android.quickstep.fallback.RecentsState; 59 import com.android.quickstep.views.RecentsView; 60 61 import org.junit.After; 62 import org.junit.Before; 63 import org.junit.Ignore; 64 import org.junit.Test; 65 import org.junit.runner.RunWith; 66 67 import java.util.Comparator; 68 import java.util.function.Consumer; 69 import java.util.function.Function; 70 71 @LargeTest 72 @RunWith(AndroidJUnit4.class) 73 public class TaplTestsQuickstep extends AbstractQuickStepTest { 74 75 private static final String CALCULATOR_APP_PACKAGE = 76 resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); 77 private static final String READ_DEVICE_CONFIG_PERMISSION = 78 "android.permission.READ_DEVICE_CONFIG"; 79 80 private enum ExpectedState { 81 82 HOME(LauncherState.NORMAL, RecentsState.HOME), 83 OVERVIEW(LauncherState.OVERVIEW, RecentsState.DEFAULT); 84 85 private final LauncherState mLauncherState; 86 private final RecentsState mRecentsState; 87 ExpectedState(LauncherState launcherState, RecentsState recentsState)88 ExpectedState(LauncherState launcherState, RecentsState recentsState) { 89 this.mLauncherState = launcherState; 90 this.mRecentsState = recentsState; 91 } 92 } 93 94 @Before setUp()95 public void setUp() throws Exception { 96 super.setUp(); 97 runOnRecentsView(recentsView -> 98 recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(true)); 99 } 100 101 @After tearDown()102 public void tearDown() { 103 runOnRecentsView(recentsView -> 104 recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false), 105 /* forTearDown= */ true); 106 } 107 startTestApps()108 public static void startTestApps() throws Exception { 109 startAppFast(getAppPackageName()); 110 startAppFast(CALCULATOR_APP_PACKAGE); 111 startTestActivity(2); 112 } 113 114 @Test 115 @NavigationModeSwitch 116 @PortraitLandscape testWorkspaceSwitchToAllApps()117 public void testWorkspaceSwitchToAllApps() { 118 assertNotNull("switchToAllApps() returned null", 119 mLauncher.getWorkspace().switchToAllApps()); 120 assertTrue("Launcher internal state is not All Apps", 121 isInState(() -> LauncherState.ALL_APPS)); 122 } 123 124 @Test 125 @PortraitLandscape testOverview()126 public void testOverview() throws Exception { 127 startTestAppsWithCheck(); 128 // mLauncher.pressHome() also tests an important case of pressing home while in background. 129 Overview overview = mLauncher.goHome().switchToOverview(); 130 assertIsInState( 131 "Launcher internal state didn't switch to Overview", ExpectedState.OVERVIEW); 132 runOnRecentsView(recentsView -> assertTrue("Don't have at least 3 tasks", 133 recentsView.getTaskViewCount() >= 3)); 134 135 // Test flinging forward and backward. 136 runOnRecentsView(recentsView -> assertEquals("Current task in Overview is not 0", 137 0, recentsView.getCurrentPage())); 138 139 overview.flingForward(); 140 assertIsInState("Launcher internal state is not Overview", ExpectedState.OVERVIEW); 141 final Integer currentTaskAfterFlingForward = 142 getFromRecentsView(RecentsView::getCurrentPage); 143 runOnRecentsView(recentsView -> assertTrue("Current task in Overview is still 0", 144 currentTaskAfterFlingForward > 0)); 145 146 overview.flingBackward(); 147 assertIsInState("Launcher internal state is not Overview", ExpectedState.OVERVIEW); 148 runOnRecentsView(recentsView -> assertTrue("Flinging back in Overview did nothing", 149 recentsView.getCurrentPage() < currentTaskAfterFlingForward)); 150 151 // Test opening a task. 152 OverviewTask task = mLauncher.goHome().switchToOverview().getCurrentTask(); 153 assertNotNull("overview.getCurrentTask() returned null (1)", task); 154 assertNotNull("OverviewTask.open returned null", task.open()); 155 assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject( 156 By.pkg(getAppPackageName()).text("TestActivity2")), 157 TestUtil.DEFAULT_UI_TIMEOUT)); 158 expectLaunchedAppState(); 159 160 // Test dismissing a task. 161 overview = mLauncher.goHome().switchToOverview(); 162 assertIsInState("Launcher internal state didn't switch to Overview", 163 ExpectedState.OVERVIEW); 164 final Integer numTasks = getFromRecentsView(RecentsView::getTaskViewCount); 165 task = overview.getCurrentTask(); 166 assertNotNull("overview.getCurrentTask() returned null (2)", task); 167 task.dismiss(); 168 runOnRecentsView(recentsView -> assertEquals( 169 "Dismissing a task didn't remove 1 task from Overview", 170 numTasks - 1, recentsView.getTaskViewCount())); 171 172 // Test dismissing all tasks. 173 mLauncher.goHome().switchToOverview().dismissAllTasks(); 174 assertIsInState("Launcher internal state is not Home", ExpectedState.HOME); 175 runOnRecentsView(recentsView -> assertEquals("Still have tasks after dismissing all", 176 0, recentsView.getTaskViewCount())); 177 } 178 179 /** 180 * Smoke test for action buttons: Presses all the buttons and makes sure no crashes occur. 181 */ 182 @Test 183 @NavigationModeSwitch 184 @PortraitLandscape testOverviewActions()185 public void testOverviewActions() throws Exception { 186 assumeFalse("Skipping Overview Actions tests for grid only overview", 187 mLauncher.isTablet() && mLauncher.isGridOnlyOverviewEnabled()); 188 startTestAppsWithCheck(); 189 OverviewActions actionsView = 190 mLauncher.goHome().switchToOverview().getOverviewActions(); 191 actionsView.clickAndDismissScreenshot(); 192 } 193 194 @Test testDismissOverviewWithEscKey()195 public void testDismissOverviewWithEscKey() throws Exception { 196 startTestAppsWithCheck(); 197 final Overview overview = mLauncher.goHome().switchToOverview(); 198 assertIsInState("Launcher internal state is not Overview", ExpectedState.OVERVIEW); 199 200 overview.dismissByEscKey(); 201 assertIsInState("Launcher internal state is not Home", ExpectedState.HOME); 202 } 203 204 @Test testDismissModalTaskAndOverviewWithEscKey()205 public void testDismissModalTaskAndOverviewWithEscKey() throws Exception { 206 startTestAppsWithCheck(); 207 final Overview overview = mLauncher.goHome().switchToOverview(); 208 209 final SelectModeButtons selectModeButtons; 210 211 if (mLauncher.isTablet() && mLauncher.isGridOnlyOverviewEnabled()) { 212 selectModeButtons = overview.getCurrentTask().tapMenu().tapSelectMenuItem(); 213 } else { 214 selectModeButtons = overview.getOverviewActions().clickSelect(); 215 } 216 217 assertTrue("Launcher internal state is not Overview Modal Task", 218 isInState(() -> LauncherState.OVERVIEW_MODAL_TASK)); 219 220 selectModeButtons.dismissByEscKey(); 221 222 assertIsInState("Launcher internal state is not Overview", ExpectedState.OVERVIEW); 223 overview.dismissByEscKey(); 224 assertIsInState("Launcher internal state is not Home", ExpectedState.HOME); 225 } 226 227 @Test testOpenOverviewWithActionPlusTabKeys()228 public void testOpenOverviewWithActionPlusTabKeys() throws Exception { 229 startTestAppsWithCheck(); 230 startAppFast(CALCULATOR_APP_PACKAGE); // Ensure Calculator is last opened app. 231 Workspace home = mLauncher.goHome(); 232 assertIsInState("Launcher state is not Home", ExpectedState.HOME); 233 234 Overview overview = home.openOverviewFromActionPlusTabKeyboardShortcut(); 235 236 assertIsInState("Launcher state is not Overview", ExpectedState.OVERVIEW); 237 overview.launchFocusedTaskByEnterKey(CALCULATOR_APP_PACKAGE); // Assert app is focused. 238 } 239 240 @Test testOpenOverviewWithRecentsKey()241 public void testOpenOverviewWithRecentsKey() throws Exception { 242 startTestAppsWithCheck(); 243 startAppFast(CALCULATOR_APP_PACKAGE); // Ensure Calculator is last opened app. 244 Workspace home = mLauncher.goHome(); 245 assertIsInState("Launcher state is not Home", ExpectedState.HOME); 246 247 Overview overview = home.openOverviewFromRecentsKeyboardShortcut(); 248 249 assertIsInState("Launcher state is not Overview", ExpectedState.OVERVIEW); 250 overview.launchFocusedTaskByEnterKey(CALCULATOR_APP_PACKAGE); // Assert app is focused. 251 } 252 253 @Test 254 @NavigationModeSwitch 255 @PortraitLandscape testSwitchToOverview()256 public void testSwitchToOverview() throws Exception { 257 startTestAppsWithCheck(); 258 assertNotNull("Workspace.switchToOverview() returned null", 259 mLauncher.goHome().switchToOverview()); 260 assertIsInState( 261 "Launcher internal state didn't switch to Overview", ExpectedState.OVERVIEW); 262 } 263 264 @Test 265 @TaskbarModeSwitch(mode = TRANSIENT) testSwitchToOverviewWithStashedTaskbar()266 public void testSwitchToOverviewWithStashedTaskbar() throws Exception { 267 try { 268 startTestAppsWithCheck(); 269 // Set ignoreTaskbarVisibility, as transient taskbar will be stashed after app launch. 270 mLauncher.setIgnoreTaskbarVisibility(true); 271 mLauncher.getLaunchedAppState().switchToOverview(); 272 } finally { 273 mLauncher.setIgnoreTaskbarVisibility(false); 274 } 275 } 276 277 @Test 278 @NavigationModeSwitch 279 @PortraitLandscape testBackground()280 public void testBackground() throws Exception { 281 startAppFast(CALCULATOR_APP_PACKAGE); 282 final LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); 283 284 assertNotNull("Background.switchToOverview() returned null", 285 launchedAppState.switchToOverview()); 286 assertIsInState( 287 "Launcher internal state didn't switch to Overview", ExpectedState.OVERVIEW); 288 } 289 290 @Test 291 @NavigationModeSwitch 292 @PortraitLandscape 293 @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/325659406 testQuickSwitchFromApp()294 public void testQuickSwitchFromApp() throws Exception { 295 startTestActivity(2); 296 startTestActivity(3); 297 startTestActivity(4); 298 299 quickSwitchToPreviousAppAndAssert(true /* toRight */); 300 assertTestActivityIsRunning(3, 301 "The first app we should have quick switched to is not running"); 302 303 quickSwitchToPreviousAppAndAssert(true /* toRight */); 304 if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) { 305 // 3-button mode toggles between 2 apps, rather than going back further. 306 assertTestActivityIsRunning(4, 307 "Second quick switch should have returned to the first app."); 308 } else { 309 assertTestActivityIsRunning(2, 310 "The second app we should have quick switched to is not running"); 311 } 312 313 quickSwitchToPreviousAppAndAssert(false /* toRight */); 314 assertTestActivityIsRunning(3, 315 "The 2nd app we should have quick switched to is not running"); 316 317 final LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); 318 launchedAppState.switchToOverview(); 319 } 320 321 @Test 322 @TaskbarModeSwitch testQuickSwitchToPreviousAppForTablet()323 public void testQuickSwitchToPreviousAppForTablet() throws Exception { 324 assumeTrue(mLauncher.isTablet()); 325 startTestActivity(2); 326 startImeTestActivity(); 327 328 // Set ignoreTaskbarVisibility to true to verify the task bar visibility explicitly. 329 mLauncher.setIgnoreTaskbarVisibility(true); 330 331 332 try { 333 boolean isTransientTaskbar = mLauncher.isTransientTaskbar(); 334 // Expect task bar invisible when the launched app was the IME activity. 335 LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); 336 if (!isTransientTaskbar && isHardwareKeyboard() && !mLauncher.isImeDocked()) { 337 launchedAppState.assertTaskbarVisible(); 338 } else { 339 launchedAppState.assertTaskbarHidden(); 340 } 341 342 // Quick-switch to the test app with swiping to right. 343 quickSwitchToPreviousAppAndAssert(true /* toRight */); 344 345 assertTestActivityIsRunning(2, 346 "The first app we should have quick switched to is not running"); 347 launchedAppState = getAndAssertLaunchedApp(); 348 if (isTransientTaskbar) { 349 launchedAppState.assertTaskbarHidden(); 350 } else { 351 // Expect taskbar visible when the launched app was the test activity. 352 launchedAppState.assertTaskbarVisible(); 353 } 354 } finally { 355 // Reset ignoreTaskbarVisibility to ensure other tests still verify it. 356 mLauncher.setIgnoreTaskbarVisibility(false); 357 } 358 } 359 360 @Test 361 @NavigationModeSwitch 362 @PortraitLandscape testQuickSwitchFromHome()363 public void testQuickSwitchFromHome() throws Exception { 364 startTestActivity(2); 365 mLauncher.goHome().quickSwitchToPreviousApp(); 366 assertTestActivityIsRunning(2, 367 "The most recent task is not running after quick switching from home"); 368 getAndAssertLaunchedApp(); 369 } 370 371 @Test 372 @PortraitLandscape 373 @NavigationModeSwitch testPressBack()374 public void testPressBack() throws Exception { 375 InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( 376 READ_DEVICE_CONFIG_PERMISSION); 377 // Debug if we need to goHome to prevent wrong previous state b/315525621 378 mLauncher.goHome(); 379 mLauncher.getWorkspace().switchToAllApps().pressBackToWorkspace(); 380 waitForState("Launcher internal state didn't switch to Home", ExpectedState.HOME); 381 382 startAppFast(CALCULATOR_APP_PACKAGE); 383 mLauncher.getLaunchedAppState().pressBackToWorkspace(); 384 waitForState("Launcher internal state didn't switch to Home", ExpectedState.HOME); 385 } 386 387 @Test 388 @PortraitLandscape 389 @TaskbarModeSwitch() 390 @Ignore("b/315376057") testOverviewForTablet()391 public void testOverviewForTablet() throws Exception { 392 assumeTrue(mLauncher.isTablet()); 393 394 for (int i = 2; i <= 14; i++) { 395 startTestActivity(i); 396 } 397 398 Overview overview = mLauncher.goHome().switchToOverview(); 399 runOnRecentsView(recentsView -> assertTrue("Don't have at least 13 tasks", 400 recentsView.getTaskViewCount() >= 13)); 401 402 // Test scroll the first task off screen 403 overview.scrollCurrentTaskOffScreen(); 404 assertIsInState("Launcher internal state is not Overview", ExpectedState.OVERVIEW); 405 runOnRecentsView(recentsView -> assertTrue("Current task in Overview is still 0", 406 recentsView.getCurrentPage() > 0)); 407 408 // Test opening the task. 409 overview.getCurrentTask().open(); 410 assertTrue("Test activity didn't open from Overview", 411 mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text( 412 mLauncher.isGridOnlyOverviewEnabled() ? "TestActivity12" 413 : "TestActivity13")), 414 TestUtil.DEFAULT_UI_TIMEOUT)); 415 416 // Scroll the task offscreen as it is now first 417 overview = mLauncher.goHome().switchToOverview(); 418 overview.scrollCurrentTaskOffScreen(); 419 assertIsInState( 420 "Launcher internal state is not Overview", ExpectedState.OVERVIEW); 421 runOnRecentsView(recentsView -> assertTrue("Current task in Overview is still 0", 422 recentsView.getCurrentPage() > 0)); 423 424 // Test dismissing the later task. 425 final Integer numTasks = getFromRecentsView(RecentsView::getTaskViewCount); 426 overview.getCurrentTask().dismiss(); 427 runOnRecentsView(recentsView -> assertEquals( 428 "Dismissing a task didn't remove 1 task from Overview", 429 numTasks - 1, recentsView.getTaskViewCount())); 430 runOnRecentsView(recentsView -> assertTrue("Grid did not rebalance after dismissal", 431 (Math.abs(recentsView.getTopRowTaskCountForTablet() 432 - recentsView.getBottomRowTaskCountForTablet()) <= 1))); 433 434 // Test dismissing more tasks. 435 assertIsInState( 436 "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW); 437 overview.getCurrentTask().dismiss(); 438 assertIsInState( 439 "Launcher internal state didn't remain in Overview", ExpectedState.OVERVIEW); 440 overview.getCurrentTask().dismiss(); 441 runOnRecentsView(recentsView -> assertTrue( 442 "Grid did not rebalance after multiple dismissals", 443 (Math.abs(recentsView.getTopRowTaskCountForTablet() 444 - recentsView.getBottomRowTaskCountForTablet()) <= 1))); 445 446 // Test dismissing all tasks. 447 mLauncher.goHome().switchToOverview().dismissAllTasks(); 448 assertIsInState("Launcher internal state is not Home", ExpectedState.HOME); 449 runOnRecentsView(recentsView -> assertEquals("Still have tasks after dismissing all", 450 0, recentsView.getTaskViewCount())); 451 } 452 453 @Test 454 @PortraitLandscape testOverviewDeadzones()455 public void testOverviewDeadzones() throws Exception { 456 startTestAppsWithCheck(); 457 458 Overview overview = mLauncher.goHome().switchToOverview(); 459 assertIsInState("Launcher internal state should be Overview", ExpectedState.OVERVIEW); 460 runOnRecentsView(recentsView -> assertTrue("Should have at least 3 tasks", 461 recentsView.getTaskViewCount() >= 3)); 462 463 // It should not dismiss overview when tapping between tasks 464 overview.touchBetweenTasks(); 465 overview = mLauncher.getOverview(); 466 assertIsInState("Launcher internal state should be Overview", ExpectedState.OVERVIEW); 467 468 // Dismiss when tapping to the right of the focused task 469 overview.touchOutsideFirstTask(); 470 assertIsInState("Launcher internal state should be Home", ExpectedState.HOME); 471 } 472 473 @Test 474 @PortraitLandscape 475 @TaskbarModeSwitch testTaskbarDeadzonesForTablet()476 public void testTaskbarDeadzonesForTablet() throws Exception { 477 assumeTrue(mLauncher.isTablet()); 478 479 startTestAppsWithCheck(); 480 481 Overview overview = mLauncher.goHome().switchToOverview(); 482 assertIsInState("Launcher internal state should be Overview", ExpectedState.OVERVIEW); 483 runOnRecentsView(recentsView -> assertTrue("Should have at least 3 tasks", 484 recentsView.getTaskViewCount() >= 3)); 485 486 if (mLauncher.isTransientTaskbar()) { 487 // On transient taskbar, it should dismiss when tapping outside taskbar bounds. 488 overview.touchTaskbarBottomCorner(/* tapRight= */ false); 489 assertIsInState("Launcher internal state should be Normal", ExpectedState.HOME); 490 491 overview = mLauncher.getWorkspace().switchToOverview(); 492 493 // On transient taskbar, it should dismiss when tapping outside taskbar bounds. 494 overview.touchTaskbarBottomCorner(/* tapRight= */ true); 495 assertIsInState("Launcher internal state should be Normal", ExpectedState.HOME); 496 } else { 497 // On persistent taskbar, it should not dismiss when tapping the taskbar 498 overview.touchTaskbarBottomCorner(/* tapRight= */ false); 499 assertIsInState("Launcher internal state should be Overview", ExpectedState.OVERVIEW); 500 501 // On persistent taskbar, it should not dismiss when tapping the taskbar 502 overview.touchTaskbarBottomCorner(/* tapRight= */ true); 503 assertIsInState("Launcher internal state should be Overview", ExpectedState.OVERVIEW); 504 } 505 } 506 507 @Test testDisableRotationCheckForPhone()508 public void testDisableRotationCheckForPhone() throws Exception { 509 assumeFalse(mLauncher.isTablet()); 510 try { 511 mLauncher.setExpectedRotationCheckEnabled(false); 512 mLauncher.setEnableRotation(false); 513 mLauncher.getDevice().setOrientationLeft(); 514 startTestActivity(7); 515 Wait.atMost("Device should not be in natural orientation", 516 () -> !mDevice.isNaturalOrientation(), mLauncher); 517 mLauncher.goHome(); 518 } finally { 519 mLauncher.setExpectedRotationCheckEnabled(true); 520 mLauncher.setEnableRotation(true); 521 mLauncher.getDevice().setOrientationNatural(); 522 } 523 } 524 525 @Test testExcludeFromRecents()526 public void testExcludeFromRecents() throws Exception { 527 startExcludeFromRecentsTestActivity(); 528 OverviewTask currentTask = getAndAssertLaunchedApp().switchToOverview().getCurrentTask(); 529 assertTrue("Can't find ExcludeFromRecentsTestActivity after entering Overview from it", 530 currentTask.containsContentDescription("ExcludeFromRecents")); 531 // Going home should clear out the excludeFromRecents task. 532 BaseOverview overview = mLauncher.goHome().switchToOverview(); 533 if (overview.hasTasks()) { 534 currentTask = overview.getCurrentTask(); 535 assertFalse("Found ExcludeFromRecentsTestActivity after entering Overview from Home", 536 currentTask.containsContentDescription("ExcludeFromRecents")); 537 } else { 538 // Presumably the test started with 0 tasks and remains that way after going home. 539 } 540 } 541 542 @Test 543 @PortraitLandscape 544 @ScreenRecordRule.ScreenRecord // TODO(b/396447643): Remove screen record. testDismissCancel()545 public void testDismissCancel() throws Exception { 546 startTestAppsWithCheck(); 547 Overview overview = mLauncher.goHome().switchToOverview(); 548 assertIsInState("Launcher internal state didn't switch to Overview", 549 ExpectedState.OVERVIEW); 550 final Integer numTasks = getFromRecentsView(RecentsView::getTaskViewCount); 551 OverviewTask task = overview.getCurrentTask(); 552 assertNotNull("overview.getCurrentTask() returned null (2)", task); 553 554 task.dismissCancel(); 555 556 runOnRecentsView(recentsView -> assertEquals( 557 "Canceling dismissing a task removed a task from Overview", 558 numTasks == null ? 0 : numTasks, recentsView.getTaskViewCount())); 559 } 560 561 @Test 562 @PortraitLandscape testDismissBottomRow()563 public void testDismissBottomRow() throws Exception { 564 assumeTrue(mLauncher.isTablet()); 565 mLauncher.goHome().switchToOverview().dismissAllTasks(); 566 startTestAppsWithCheck(); 567 Overview overview = mLauncher.goHome().switchToOverview(); 568 assertIsInState("Launcher internal state didn't switch to Overview", 569 ExpectedState.OVERVIEW); 570 final Integer numTasks = getFromRecentsView(RecentsView::getTaskViewCount); 571 OverviewTask bottomTask = overview.getCurrentTasksForTablet().stream().max( 572 Comparator.comparingInt(OverviewTask::getTaskCenterY)).get(); 573 assertNotNull("bottomTask null", bottomTask); 574 575 bottomTask.dismiss(); 576 577 runOnRecentsView(recentsView -> assertEquals( 578 "Dismissing a bottomTask didn't remove 1 bottomTask from Overview", 579 numTasks - 1, recentsView.getTaskViewCount())); 580 } 581 582 @Test 583 @PortraitLandscape testDismissLastGridRow()584 public void testDismissLastGridRow() throws Exception { 585 assumeTrue(mLauncher.isTablet()); 586 mLauncher.goHome().switchToOverview().dismissAllTasks(); 587 startTestAppsWithCheck(); 588 startTestActivity(3); 589 startTestActivity(4); 590 runOnRecentsView( 591 recentsView -> assertNotEquals("Grid overview should have unequal row counts", 592 recentsView.getTopRowTaskCountForTablet(), 593 recentsView.getBottomRowTaskCountForTablet())); 594 Overview overview = mLauncher.goHome().switchToOverview(); 595 assertIsInState("Launcher internal state didn't switch to Overview", 596 ExpectedState.OVERVIEW); 597 overview.flingForwardUntilClearAllVisible(); 598 assertTrue("Clear All not visible.", overview.isClearAllVisible()); 599 final Integer numTasks = getFromRecentsView(RecentsView::getTaskViewCount); 600 OverviewTask lastGridTask = overview.getCurrentTasksForTablet().stream().min( 601 Comparator.comparingInt(OverviewTask::getTaskCenterX)).get(); 602 assertNotNull("lastGridTask null.", lastGridTask); 603 604 lastGridTask.dismiss(); 605 606 runOnRecentsView(recentsView -> assertEquals( 607 "Dismissing a lastGridTask didn't remove 1 lastGridTask from Overview", 608 numTasks - 1, recentsView.getTaskViewCount())); 609 runOnRecentsView(recentsView -> assertEquals("Grid overview should have equal row counts.", 610 recentsView.getTopRowTaskCountForTablet(), 611 recentsView.getBottomRowTaskCountForTablet())); 612 assertTrue("Clear All not visible.", overview.isClearAllVisible()); 613 } 614 startTestAppsWithCheck()615 private void startTestAppsWithCheck() throws Exception { 616 startTestApps(); 617 expectLaunchedAppState(); 618 } 619 quickSwitchToPreviousAppAndAssert(boolean toRight)620 private void quickSwitchToPreviousAppAndAssert(boolean toRight) { 621 final LaunchedAppState launchedAppState = getAndAssertLaunchedApp(); 622 if (toRight) { 623 launchedAppState.quickSwitchToPreviousApp(); 624 } else { 625 launchedAppState.quickSwitchToPreviousAppSwipeLeft(); 626 } 627 628 // While enable shell transition, Launcher can be resumed due to transient launch. 629 waitForLauncherCondition("Launcher shouldn't stay in resume forever", 630 this::isInLaunchedApp, 3000 /* timeout */); 631 } 632 isHardwareKeyboard()633 private boolean isHardwareKeyboard() { 634 return Configuration.KEYBOARD_QWERTY 635 == mTargetContext.getResources().getConfiguration().keyboard; 636 } 637 assertIsInState( @onNull String failureMessage, @NonNull ExpectedState expectedState)638 private void assertIsInState( 639 @NonNull String failureMessage, @NonNull ExpectedState expectedState) { 640 assertTrue(failureMessage, enableLauncherOverviewInWindow() 641 ? isInRecentsWindowState(() -> expectedState.mRecentsState) 642 : isInState(() -> expectedState.mLauncherState)); 643 } 644 waitForState( @onNull String failureMessage, @NonNull ExpectedState expectedState)645 private void waitForState( 646 @NonNull String failureMessage, @NonNull ExpectedState expectedState) { 647 if (enableLauncherOverviewInWindow()) { 648 waitForRecentsWindowState(failureMessage, () -> expectedState.mRecentsState); 649 } else { 650 waitForState(failureMessage, () -> expectedState.mLauncherState); 651 } 652 } 653 expectLaunchedAppState()654 private void expectLaunchedAppState() { 655 executeOnLauncher(launcher -> assertTrue( 656 "Launcher activity is the top activity; expecting another activity to be the top " 657 + "one", 658 isInLaunchedApp(launcher))); 659 } 660 getFromRecentsView(Function<RecentsView, T> f)661 private <T> T getFromRecentsView(Function<RecentsView, T> f) { 662 return getFromRecentsView(f, false); 663 } 664 getFromRecentsView(Function<RecentsView, T> f, boolean forTearDown)665 private <T> T getFromRecentsView(Function<RecentsView, T> f, boolean forTearDown) { 666 if (enableLauncherOverviewInWindow()) { 667 return getFromRecentsWindow(recentsWindowManager -> 668 (forTearDown && recentsWindowManager == null) 669 ? null : f.apply(recentsWindowManager.getOverviewPanel())); 670 } else { 671 return getFromLauncher(launcher -> (forTearDown && launcher == null) 672 ? null : f.apply(launcher.getOverviewPanel())); 673 } 674 } 675 runOnRecentsView(Consumer<RecentsView> f)676 private void runOnRecentsView(Consumer<RecentsView> f) { 677 runOnRecentsView(f, false); 678 } 679 runOnRecentsView(Consumer<RecentsView> f, boolean forTearDown)680 private void runOnRecentsView(Consumer<RecentsView> f, boolean forTearDown) { 681 getFromRecentsView(recentsView -> { 682 if (forTearDown && recentsView == null) { 683 return null; 684 } 685 f.accept(recentsView); 686 return null; 687 }, forTearDown); 688 } 689 } 690