1 /* 2 * Copyright (C) 2019 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.launcher3.ui; 18 19 import static androidx.test.InstrumentationRegistry.getInstrumentation; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertTrue; 26 27 import androidx.test.filters.LargeTest; 28 import androidx.test.runner.AndroidJUnit4; 29 30 import com.android.launcher3.Launcher; 31 import com.android.launcher3.LauncherState; 32 import com.android.launcher3.popup.ArrowPopup; 33 import com.android.launcher3.tapl.AllApps; 34 import com.android.launcher3.tapl.AppIcon; 35 import com.android.launcher3.tapl.AppIconMenu; 36 import com.android.launcher3.tapl.AppIconMenuItem; 37 import com.android.launcher3.tapl.Folder; 38 import com.android.launcher3.tapl.FolderIcon; 39 import com.android.launcher3.tapl.Widgets; 40 import com.android.launcher3.tapl.Workspace; 41 import com.android.launcher3.widget.picker.WidgetsFullSheet; 42 import com.android.launcher3.widget.picker.WidgetsRecyclerView; 43 44 import org.junit.Before; 45 import org.junit.Ignore; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 @LargeTest 50 @RunWith(AndroidJUnit4.class) 51 public class TaplTestsLauncher3 extends AbstractLauncherUiTest { 52 private static final String APP_NAME = "LauncherTestApp"; 53 54 @Before setUp()55 public void setUp() throws Exception { 56 super.setUp(); 57 initialize(this); 58 } 59 initialize(AbstractLauncherUiTest test)60 public static void initialize(AbstractLauncherUiTest test) throws Exception { 61 test.clearLauncherData(); 62 test.mDevice.pressHome(); 63 test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); 64 test.waitForState("Launcher internal state didn't switch to Home", 65 () -> LauncherState.NORMAL); 66 test.waitForResumed("Launcher internal state is still Background"); 67 // Check that we switched to home. 68 test.mLauncher.getWorkspace(); 69 AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher); 70 } 71 72 // Please don't add negative test cases for methods that fail only after a long wait. expectFail(String message, Runnable action)73 public static void expectFail(String message, Runnable action) { 74 boolean failed = false; 75 try { 76 action.run(); 77 } catch (AssertionError e) { 78 failed = true; 79 } 80 assertTrue(message, failed); 81 } 82 isWorkspaceScrollable(Launcher launcher)83 public static boolean isWorkspaceScrollable(Launcher launcher) { 84 return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount(); 85 } 86 getCurrentWorkspacePage(Launcher launcher)87 private int getCurrentWorkspacePage(Launcher launcher) { 88 return launcher.getWorkspace().getCurrentPage(); 89 } 90 getWidgetsView(Launcher launcher)91 private WidgetsRecyclerView getWidgetsView(Launcher launcher) { 92 return WidgetsFullSheet.getWidgetsView(launcher); 93 } 94 95 @Test testDevicePressMenu()96 public void testDevicePressMenu() throws Exception { 97 mDevice.pressMenu(); 98 mDevice.waitForIdle(); 99 executeOnLauncher( 100 launcher -> assertNotNull("Launcher internal state didn't switch to Showing Menu", 101 launcher.getOptionsPopup())); 102 // Check that pressHome works when the menu is shown. 103 mLauncher.pressHome(); 104 } 105 106 @Test testPressHomeOnAllAppsContextMenu()107 public void testPressHomeOnAllAppsContextMenu() throws Exception { 108 final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); 109 allApps.freeze(); 110 try { 111 allApps.getAppIcon("TestActivity7").openMenu(); 112 } finally { 113 allApps.unfreeze(); 114 } 115 mLauncher.pressHome(); 116 } 117 runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps)118 public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) { 119 allApps.freeze(); 120 try { 121 assertNotNull("allApps parameter is null", allApps); 122 123 assertTrue( 124 "Launcher internal state is not All Apps", 125 test.isInState(() -> LauncherState.ALL_APPS)); 126 127 // Test flinging forward and backward. 128 test.executeOnLauncher(launcher -> assertEquals( 129 "All Apps started in already scrolled state", 0, 130 test.getAllAppsScroll(launcher))); 131 132 allApps.flingForward(); 133 assertTrue("Launcher internal state is not All Apps", 134 test.isInState(() -> LauncherState.ALL_APPS)); 135 final Integer flingForwardY = test.getFromLauncher( 136 launcher -> test.getAllAppsScroll(launcher)); 137 test.executeOnLauncher( 138 launcher -> assertTrue("flingForward() didn't scroll App Apps", 139 flingForwardY > 0)); 140 141 allApps.flingBackward(); 142 assertTrue( 143 "Launcher internal state is not All Apps", 144 test.isInState(() -> LauncherState.ALL_APPS)); 145 final Integer flingBackwardY = test.getFromLauncher( 146 launcher -> test.getAllAppsScroll(launcher)); 147 test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps", 148 flingBackwardY < flingForwardY)); 149 150 // Test scrolling down to YouTube. 151 assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube")); 152 // Test scrolling up to Camera. 153 assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera")); 154 // Test failing to find a non-existing app. 155 final AllApps allAppsFinal = allApps; 156 expectFail("All apps: could find a non-existing app", 157 () -> allAppsFinal.getAppIcon("NO APP")); 158 159 assertTrue( 160 "Launcher internal state is not All Apps", 161 test.isInState(() -> LauncherState.ALL_APPS)); 162 } finally { 163 allApps.unfreeze(); 164 } 165 } 166 167 @Test 168 @PortraitLandscape testWorkspaceSwitchToAllApps()169 public void testWorkspaceSwitchToAllApps() { 170 assertNotNull("switchToAllApps() returned null", 171 mLauncher.getWorkspace().switchToAllApps()); 172 assertTrue("Launcher internal state is not All Apps", 173 isInState(() -> LauncherState.ALL_APPS)); 174 } 175 176 @Test testWorkspace()177 public void testWorkspace() throws Exception { 178 final Workspace workspace = mLauncher.getWorkspace(); 179 180 // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there. 181 executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable", 182 isWorkspaceScrollable(launcher))); 183 assertNull("Chrome app was found on empty workspace", 184 workspace.tryGetWorkspaceAppIcon("Chrome")); 185 186 workspace.ensureWorkspaceIsScrollable(); 187 188 executeOnLauncher( 189 launcher -> assertEquals( 190 "Ensuring workspace scrollable didn't switch to next screen", 191 workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher))); 192 executeOnLauncher( 193 launcher -> assertTrue("ensureScrollable didn't make workspace scrollable", 194 isWorkspaceScrollable(launcher))); 195 assertNotNull("ensureScrollable didn't add Chrome app", 196 workspace.getWorkspaceAppIcon("Chrome")); 197 198 // Test flinging workspace. 199 workspace.flingBackward(); 200 assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); 201 executeOnLauncher( 202 launcher -> assertEquals("Flinging back didn't switch workspace to page #0", 203 0, getCurrentWorkspacePage(launcher))); 204 205 workspace.flingForward(); 206 executeOnLauncher( 207 launcher -> assertEquals("Flinging forward didn't switch workspace to next screen", 208 workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher))); 209 assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); 210 211 // Test starting a workspace app. 212 final AppIcon app = workspace.getWorkspaceAppIcon("Chrome"); 213 assertNotNull("No Chrome app in workspace", app); 214 } 215 runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps)216 public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) { 217 allApps.freeze(); 218 try { 219 final AppIcon app = allApps.getAppIcon("TestActivity7"); 220 assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); 221 test.executeOnLauncher(launcher -> assertTrue( 222 "Launcher activity is the top activity; expecting another activity to be the " 223 + "top " 224 + "one", 225 test.isInBackground(launcher))); 226 } finally { 227 allApps.unfreeze(); 228 } 229 } 230 231 @Test 232 @PortraitLandscape testAppIconLaunchFromAllAppsFromHome()233 public void testAppIconLaunchFromAllAppsFromHome() throws Exception { 234 final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); 235 assertTrue("Launcher internal state is not All Apps", 236 isInState(() -> LauncherState.ALL_APPS)); 237 238 runIconLaunchFromAllAppsTest(this, allApps); 239 } 240 241 @Test 242 @PortraitLandscape testWidgets()243 public void testWidgets() throws Exception { 244 // Test opening widgets. 245 executeOnLauncher(launcher -> 246 assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null)); 247 Widgets widgets = mLauncher.getWorkspace().openAllWidgets(); 248 assertNotNull("openAllWidgets() returned null", widgets); 249 widgets = mLauncher.getAllWidgets(); 250 assertNotNull("getAllWidgets() returned null", widgets); 251 executeOnLauncher(launcher -> 252 assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown())); 253 executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening", 254 0, getWidgetsScroll(launcher))); 255 256 // Test flinging widgets. 257 widgets.flingForward(); 258 Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher)); 259 executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets", 260 flingForwardY > 0)); 261 262 widgets.flingBackward(); 263 executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets", 264 getWidgetsScroll(launcher) < flingForwardY)); 265 266 mLauncher.pressHome(); 267 waitForLauncherCondition("Widgets were not closed", 268 launcher -> getWidgetsView(launcher) == null); 269 } 270 getWidgetsScroll(Launcher launcher)271 private int getWidgetsScroll(Launcher launcher) { 272 return getWidgetsView(launcher).getCurrentScrollY(); 273 } 274 isOptionsPopupVisible(Launcher launcher)275 private boolean isOptionsPopupVisible(Launcher launcher) { 276 final ArrowPopup<?> popup = launcher.getOptionsPopup(); 277 return popup != null && popup.isShown(); 278 } 279 280 @Test 281 @PortraitLandscape testLaunchMenuItem()282 public void testLaunchMenuItem() throws Exception { 283 final AllApps allApps = mLauncher. 284 getWorkspace(). 285 switchToAllApps(); 286 allApps.freeze(); 287 try { 288 final AppIconMenu menu = allApps. 289 getAppIcon(APP_NAME). 290 openDeepShortcutMenu(); 291 292 executeOnLauncher( 293 launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu", 294 isOptionsPopupVisible(launcher))); 295 296 final AppIconMenuItem menuItem = menu.getMenuItem(1); 297 assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText()); 298 menuItem.launch(getAppPackageName()); 299 } finally { 300 allApps.unfreeze(); 301 } 302 } 303 304 @Test 305 @PortraitLandscape testDragAppIcon()306 public void testDragAppIcon() throws Throwable { 307 // 1. Open all apps and wait for load complete. 308 // 2. Drag icon to homescreen. 309 // 3. Verify that the icon works on homescreen. 310 final AllApps allApps = mLauncher.getWorkspace(). 311 switchToAllApps(); 312 allApps.freeze(); 313 try { 314 allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); 315 mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); 316 } finally { 317 allApps.unfreeze(); 318 } 319 executeOnLauncher(launcher -> assertTrue( 320 "Launcher activity is the top activity; expecting another activity to be the top " 321 + "one", 322 isInBackground(launcher))); 323 } 324 325 @Test 326 @PortraitLandscape testDragShortcut()327 public void testDragShortcut() throws Throwable { 328 // 1. Open all apps and wait for load complete. 329 // 2. Find the app and long press it to show shortcuts. 330 // 3. Press icon center until shortcuts appear 331 final AllApps allApps = mLauncher 332 .getWorkspace() 333 .switchToAllApps(); 334 allApps.freeze(); 335 try { 336 final AppIconMenu menu = allApps 337 .getAppIcon(APP_NAME) 338 .openDeepShortcutMenu(); 339 final AppIconMenuItem menuItem0 = menu.getMenuItem(0); 340 final AppIconMenuItem menuItem2 = menu.getMenuItem(2); 341 342 final AppIconMenuItem menuItem; 343 344 final String expectedShortcutName = "Shortcut 3"; 345 if (menuItem0.getText().equals(expectedShortcutName)) { 346 menuItem = menuItem0; 347 } else { 348 final String shortcutName2 = menuItem2.getText(); 349 assertEquals("Wrong menu item", expectedShortcutName, shortcutName2); 350 menuItem = menuItem2; 351 } 352 353 menuItem.dragToWorkspace(false, false); 354 mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName) 355 .launch(getAppPackageName()); 356 } finally { 357 allApps.unfreeze(); 358 } 359 } 360 createShortcutIfNotExist(String name)361 private AppIcon createShortcutIfNotExist(String name) { 362 AppIcon appIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name); 363 if (appIcon == null) { 364 AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); 365 allApps.freeze(); 366 try { 367 appIcon = allApps.getAppIcon(name); 368 appIcon.dragToWorkspace(false, false); 369 } finally { 370 allApps.unfreeze(); 371 } 372 appIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name); 373 } 374 return appIcon; 375 } 376 377 @Ignore("b/205014516") 378 @Test 379 @PortraitLandscape testDragToFolder()380 public void testDragToFolder() throws Exception { 381 final AppIcon playStoreIcon = createShortcutIfNotExist("Play Store"); 382 final AppIcon gmailIcon = createShortcutIfNotExist("Gmail"); 383 384 FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon); 385 386 Folder folder = folderIcon.open(); 387 folder.getAppIcon("Play Store"); 388 folder.getAppIcon("Gmail"); 389 Workspace workspace = folder.close(); 390 391 assertNull("Gmail should be moved to a folder.", 392 workspace.tryGetWorkspaceAppIcon("Gmail")); 393 assertNull("Play Store should be moved to a folder.", 394 workspace.tryGetWorkspaceAppIcon("Play Store")); 395 396 final AppIcon youTubeIcon = createShortcutIfNotExist("YouTube"); 397 398 folderIcon = youTubeIcon.dragToIcon(folderIcon); 399 folder = folderIcon.open(); 400 folder.getAppIcon("YouTube"); 401 folder.close(); 402 } 403 404 @Ignore("b/205027405") 405 @Test 406 @PortraitLandscape testPressBack()407 public void testPressBack() throws Exception { 408 mLauncher.getWorkspace().switchToAllApps(); 409 mLauncher.pressBack(); 410 mLauncher.getWorkspace(); 411 waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); 412 413 AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); 414 allApps.freeze(); 415 try { 416 allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); 417 } finally { 418 allApps.unfreeze(); 419 } 420 mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); 421 mLauncher.pressBack(); 422 mLauncher.getWorkspace(); 423 waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); 424 } 425 426 @Test 427 @PortraitLandscape testDeleteFromWorkspace()428 public void testDeleteFromWorkspace() throws Exception { 429 // test delete both built-in apps and user-installed app from workspace 430 for (String appName : new String[] {"Gmail", "Play Store", APP_NAME}) { 431 final AppIcon appIcon = createShortcutIfNotExist(appName); 432 Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(appIcon); 433 assertNull(appName + " app was found after being deleted from workspace", 434 workspace.tryGetWorkspaceAppIcon(appName)); 435 } 436 } 437 getAppPackageName()438 public static String getAppPackageName() { 439 return getInstrumentation().getContext().getPackageName(); 440 } 441 } 442