• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
22 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
23 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertTrue;
31 import static org.junit.Assume.assumeFalse;
32 import static org.junit.Assume.assumeTrue;
33 
34 import android.content.Intent;
35 import android.graphics.Point;
36 import android.os.SystemClock;
37 import android.platform.test.annotations.PlatinumTest;
38 import android.util.Log;
39 
40 import androidx.test.filters.FlakyTest;
41 import androidx.test.filters.LargeTest;
42 import androidx.test.platform.app.InstrumentationRegistry;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import com.android.launcher3.Launcher;
46 import com.android.launcher3.LauncherState;
47 import com.android.launcher3.config.FeatureFlags;
48 import com.android.launcher3.popup.ArrowPopup;
49 import com.android.launcher3.tapl.AllApps;
50 import com.android.launcher3.tapl.AppIcon;
51 import com.android.launcher3.tapl.AppIconMenu;
52 import com.android.launcher3.tapl.AppIconMenuItem;
53 import com.android.launcher3.tapl.Folder;
54 import com.android.launcher3.tapl.FolderIcon;
55 import com.android.launcher3.tapl.HomeAllApps;
56 import com.android.launcher3.tapl.HomeAppIcon;
57 import com.android.launcher3.tapl.HomeAppIconMenuItem;
58 import com.android.launcher3.tapl.Widgets;
59 import com.android.launcher3.tapl.Workspace;
60 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
61 import com.android.launcher3.util.LauncherLayoutBuilder;
62 import com.android.launcher3.util.TestUtil;
63 import com.android.launcher3.util.Wait;
64 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
65 import com.android.launcher3.util.rule.TISBindRule;
66 import com.android.launcher3.util.rule.TestStabilityRule.Stability;
67 import com.android.launcher3.widget.picker.WidgetsFullSheet;
68 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
69 
70 import org.junit.After;
71 import org.junit.Before;
72 import org.junit.Ignore;
73 import org.junit.Rule;
74 import org.junit.Test;
75 import org.junit.runner.RunWith;
76 
77 import java.io.IOException;
78 import java.util.Map;
79 
80 @LargeTest
81 @RunWith(AndroidJUnit4.class)
82 public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
83     private static final String APP_NAME = "LauncherTestApp";
84     private static final String DUMMY_APP_NAME = "Aardwolf";
85     private static final String MAPS_APP_NAME = "Maps";
86     private static final String STORE_APP_NAME = "Play Store";
87     private static final String GMAIL_APP_NAME = "Gmail";
88     private static final String READ_DEVICE_CONFIG_PERMISSION =
89             "android.permission.READ_DEVICE_CONFIG";
90 
91     @Rule
92     public TISBindRule mTISBindRule = new TISBindRule();
93 
94     private AutoCloseable mLauncherLayout;
95 
96     @Before
setUp()97     public void setUp() throws Exception {
98         super.setUp();
99         initialize(this);
100     }
101 
initialize(AbstractLauncherUiTest test)102     public static void initialize(AbstractLauncherUiTest test) throws Exception {
103         initialize(test, false);
104     }
105 
initialize( AbstractLauncherUiTest test, boolean clearWorkspace)106     public static void initialize(
107             AbstractLauncherUiTest test, boolean clearWorkspace) throws Exception {
108         test.reinitializeLauncherData(clearWorkspace);
109         test.mDevice.pressHome();
110         test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
111         test.waitForState("Launcher internal state didn't switch to Home",
112                 () -> LauncherState.NORMAL);
113         test.waitForResumed("Launcher internal state is still Background");
114         // Check that we switched to home.
115         test.mLauncher.getWorkspace();
116         AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher);
117     }
118 
119     @After
tearDown()120     public void tearDown() throws Exception {
121         if (mLauncherLayout != null) {
122             mLauncherLayout.close();
123         }
124     }
125 
126     // Please don't add negative test cases for methods that fail only after a long wait.
expectFail(String message, Runnable action)127     public static void expectFail(String message, Runnable action) {
128         boolean failed = false;
129         try {
130             action.run();
131         } catch (AssertionError e) {
132             failed = true;
133         }
134         assertTrue(message, failed);
135     }
136 
isWorkspaceScrollable(Launcher launcher)137     public static boolean isWorkspaceScrollable(Launcher launcher) {
138         return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount();
139     }
140 
getCurrentWorkspacePage(Launcher launcher)141     private int getCurrentWorkspacePage(Launcher launcher) {
142         return launcher.getWorkspace().getCurrentPage();
143     }
144 
getWidgetsView(Launcher launcher)145     private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
146         return WidgetsFullSheet.getWidgetsView(launcher);
147     }
148 
149     @Test
testDevicePressMenu()150     public void testDevicePressMenu() throws Exception {
151         mDevice.pressMenu();
152         mDevice.waitForIdle();
153         executeOnLauncher(
154                 launcher -> assertNotNull("Launcher internal state didn't switch to Showing Menu",
155                         launcher.getOptionsPopup()));
156         // Check that pressHome works when the menu is shown.
157         mLauncher.goHome();
158     }
159 
160     @Test
testPressHomeOnAllAppsContextMenu()161     public void testPressHomeOnAllAppsContextMenu() throws Exception {
162         final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
163         allApps.freeze();
164         try {
165             allApps.getAppIcon("TestActivity7").openMenu();
166         } finally {
167             allApps.unfreeze();
168         }
169         mLauncher.goHome();
170     }
171 
runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps)172     public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
173         allApps.freeze();
174         try {
175             assertNotNull("allApps parameter is null", allApps);
176 
177             assertTrue(
178                     "Launcher internal state is not All Apps",
179                     test.isInState(() -> LauncherState.ALL_APPS));
180 
181             // Test flinging forward and backward.
182             test.executeOnLauncher(launcher -> assertEquals(
183                     "All Apps started in already scrolled state", 0,
184                     test.getAllAppsScroll(launcher)));
185 
186             allApps.flingForward();
187             assertTrue("Launcher internal state is not All Apps",
188                     test.isInState(() -> LauncherState.ALL_APPS));
189             final Integer flingForwardY = test.getFromLauncher(
190                     launcher -> test.getAllAppsScroll(launcher));
191             test.executeOnLauncher(
192                     launcher -> assertTrue("flingForward() didn't scroll App Apps",
193                             flingForwardY > 0));
194 
195             allApps.flingBackward();
196             assertTrue(
197                     "Launcher internal state is not All Apps",
198                     test.isInState(() -> LauncherState.ALL_APPS));
199             final Integer flingBackwardY = test.getFromLauncher(
200                     launcher -> test.getAllAppsScroll(launcher));
201             test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
202                     flingBackwardY < flingForwardY));
203 
204             // Test scrolling down to YouTube.
205             assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube"));
206             // Test scrolling up to Camera.
207             assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera"));
208             // Test failing to find a non-existing app.
209             final AllApps allAppsFinal = allApps;
210             expectFail("All apps: could find a non-existing app",
211                     () -> allAppsFinal.getAppIcon("NO APP"));
212 
213             assertTrue(
214                     "Launcher internal state is not All Apps",
215                     test.isInState(() -> LauncherState.ALL_APPS));
216         } finally {
217             allApps.unfreeze();
218         }
219     }
220 
221     @Test
222     @PortraitLandscape
testWorkspaceSwitchToAllApps()223     public void testWorkspaceSwitchToAllApps() {
224         assertNotNull("switchToAllApps() returned null",
225                 mLauncher.getWorkspace().switchToAllApps());
226         assertTrue("Launcher internal state is not All Apps",
227                 isInState(() -> LauncherState.ALL_APPS));
228     }
229 
230     @Test
231     @PortraitLandscape
testAllAppsSwitchToWorkspace()232     public void testAllAppsSwitchToWorkspace() {
233         assertNotNull("switchToWorkspace() returned null",
234                 mLauncher.getWorkspace().switchToAllApps()
235                         .switchToWorkspace(/* swipeDown= */ true));
236         assertTrue("Launcher internal state is not Workspace",
237                 isInState(() -> LauncherState.NORMAL));
238     }
239 
240     @Test
241     @PortraitLandscape
testAllAppsSwipeUpToWorkspace()242     public void testAllAppsSwipeUpToWorkspace() {
243         assertNotNull("testAllAppsSwipeUpToWorkspace() returned null",
244                 mLauncher.getWorkspace().switchToAllApps()
245                         .switchToWorkspace(/* swipeDown= */ false));
246         assertTrue("Launcher internal state is not Workspace",
247                 isInState(() -> LauncherState.NORMAL));
248     }
249 
250     @Test
251     @PortraitLandscape
testAllAppsDeadzoneForTablet()252     public void testAllAppsDeadzoneForTablet() throws Exception {
253         assumeTrue(mLauncher.isTablet());
254 
255         mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
256                 true /* tapRight */);
257         mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
258                 false /* tapRight */);
259     }
260 
261     @PlatinumTest(focusArea = "launcher")
262     @Test
263     @ScreenRecord // b/202433017
testWorkspace()264     public void testWorkspace() throws Exception {
265         // Set workspace  that includes the chrome Activity app icon on the hotseat.
266         LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
267                 .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main");
268         mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
269         reinitializeLauncherData();
270 
271         final Workspace workspace = mLauncher.getWorkspace();
272 
273         // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
274         executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
275                 isWorkspaceScrollable(launcher)));
276         assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(),
277                 workspace.getPageCount());
278         workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome");
279         workspace.ensureWorkspaceIsScrollable();
280 
281         executeOnLauncher(
282                 launcher -> assertEquals(
283                         "Ensuring workspace scrollable didn't switch to next screen",
284                         workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
285         executeOnLauncher(
286                 launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
287                         isWorkspaceScrollable(launcher)));
288         assertNotNull("ensureScrollable didn't add Chrome app",
289                 workspace.getWorkspaceAppIcon("Chrome"));
290 
291         // Test flinging workspace.
292         workspace.flingBackward();
293         assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
294         executeOnLauncher(
295                 launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
296                         0, getCurrentWorkspacePage(launcher)));
297 
298         workspace.flingForward();
299         executeOnLauncher(
300                 launcher -> assertEquals("Flinging forward didn't switch workspace to next screen",
301                         workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
302         assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
303 
304         // Test starting a workspace app.
305         final HomeAppIcon app = workspace.getWorkspaceAppIcon("Chrome");
306         assertNotNull("No Chrome app in workspace", app);
307     }
308 
runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps)309     public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
310         allApps.freeze();
311         try {
312             final AppIcon app = allApps.getAppIcon("TestActivity7");
313             assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
314             test.executeOnLauncher(launcher -> assertTrue(
315                     "Launcher activity is the top activity; expecting another activity to be the "
316                             + "top one",
317                     test.isInLaunchedApp(launcher)));
318         } finally {
319             allApps.unfreeze();
320         }
321     }
322 
323     @Test
324     @PortraitLandscape
testAppIconLaunchFromAllAppsFromHome()325     public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
326         final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
327         assertTrue("Launcher internal state is not All Apps",
328                 isInState(() -> LauncherState.ALL_APPS));
329 
330         runIconLaunchFromAllAppsTest(this, allApps);
331     }
332 
333     @Test
334     @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/293191790
335     @ScreenRecord
336     @PortraitLandscape
testWidgets()337     public void testWidgets() throws Exception {
338         // Test opening widgets.
339         executeOnLauncher(launcher ->
340                 assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
341         Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
342         assertNotNull("openAllWidgets() returned null", widgets);
343         widgets = mLauncher.getAllWidgets();
344         assertNotNull("getAllWidgets() returned null", widgets);
345         executeOnLauncher(launcher ->
346                 assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
347         executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
348                 0, getWidgetsScroll(launcher)));
349 
350         // Test flinging widgets.
351         widgets.flingForward();
352         Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
353         executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
354                 flingForwardY > 0));
355 
356         widgets.flingBackward();
357         executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
358                 getWidgetsScroll(launcher) < flingForwardY));
359 
360         mLauncher.goHome();
361         waitForLauncherCondition("Widgets were not closed",
362                 launcher -> getWidgetsView(launcher) == null);
363     }
364 
getWidgetsScroll(Launcher launcher)365     private int getWidgetsScroll(Launcher launcher) {
366         return getWidgetsView(launcher).computeVerticalScrollOffset();
367     }
368 
isOptionsPopupVisible(Launcher launcher)369     private boolean isOptionsPopupVisible(Launcher launcher) {
370         final ArrowPopup<?> popup = launcher.getOptionsPopup();
371         return popup != null && popup.isShown();
372     }
373 
374     @Test
375     @PortraitLandscape
376     @PlatinumTest(focusArea = "launcher")
testLaunchMenuItem()377     public void testLaunchMenuItem() throws Exception {
378         final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
379         allApps.freeze();
380         try {
381             final AppIconMenu menu = allApps.
382                     getAppIcon(APP_NAME).
383                     openDeepShortcutMenu();
384 
385             executeOnLauncher(
386                     launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
387                             isOptionsPopupVisible(launcher)));
388 
389             final AppIconMenuItem menuItem = menu.getMenuItem(1);
390             assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText());
391             menuItem.launch(getAppPackageName());
392         } finally {
393             allApps.unfreeze();
394         }
395     }
396 
397     @Test
testLaunchHomeScreenMenuItem()398     public void testLaunchHomeScreenMenuItem() {
399         // Drag the test app icon to home screen and open short cut menu from the icon
400         final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
401         allApps.freeze();
402         try {
403             allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
404             final AppIconMenu menu = mLauncher.getWorkspace().getWorkspaceAppIcon(
405                     APP_NAME).openDeepShortcutMenu();
406 
407             executeOnLauncher(
408                     launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
409                             isOptionsPopupVisible(launcher)));
410 
411             final AppIconMenuItem menuItem = menu.getMenuItem(1);
412             assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText());
413             menuItem.launch(getAppPackageName());
414         } finally {
415             allApps.unfreeze();
416         }
417     }
418 
419     @PlatinumTest(focusArea = "launcher")
420     @Test
421     @PortraitLandscape
422     @ScreenRecord // b/256898879
testDragAppIcon()423     public void testDragAppIcon() throws Throwable {
424         // 1. Open all apps and wait for load complete.
425         // 2. Drag icon to homescreen.
426         // 3. Verify that the icon works on homescreen.
427         final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
428         allApps.freeze();
429         try {
430             allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
431             mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
432         } finally {
433             allApps.unfreeze();
434         }
435         executeOnLauncher(launcher -> assertTrue(
436                 "Launcher activity is the top activity; expecting another activity to be the top "
437                         + "one",
438                 isInLaunchedApp(launcher)));
439     }
440 
441     @Test
442     @PortraitLandscape
443     @PlatinumTest(focusArea = "launcher")
testDragShortcut()444     public void testDragShortcut() throws Throwable {
445         // 1. Open all apps and wait for load complete.
446         // 2. Find the app and long press it to show shortcuts.
447         // 3. Press icon center until shortcuts appear
448         final HomeAllApps allApps = mLauncher
449                 .getWorkspace()
450                 .switchToAllApps();
451         allApps.freeze();
452         try {
453             final HomeAppIconMenuItem menuItem = allApps
454                     .getAppIcon(APP_NAME)
455                     .openDeepShortcutMenu()
456                     .getMenuItem(0);
457             final String actualShortcutName = menuItem.getText();
458             final String expectedShortcutName = "Shortcut 1";
459 
460             assertEquals(expectedShortcutName, actualShortcutName);
461             menuItem.dragToWorkspace(false, false);
462             mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
463                     .launch(getAppPackageName());
464         } finally {
465             allApps.unfreeze();
466         }
467     }
468 
469     @Test
470     @PortraitLandscape
471     @ScreenRecord
472     @Ignore // b/233075289
473     @PlatinumTest(focusArea = "launcher")
testDragToFolder()474     public void testDragToFolder() {
475         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
476         // on tablets or phones due to difference in resolution.
477         final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1);
478         final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
479 
480         FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
481         Folder folder = folderIcon.open();
482         folder.getAppIcon(STORE_APP_NAME);
483         folder.getAppIcon(GMAIL_APP_NAME);
484         Workspace workspace = folder.close();
485 
486         workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.",
487                 STORE_APP_NAME);
488         workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.",
489                 GMAIL_APP_NAME);
490 
491         final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
492         folderIcon = mapIcon.dragToIcon(folderIcon);
493         folder = folderIcon.open();
494         folder.getAppIcon(MAPS_APP_NAME);
495         workspace = folder.close();
496 
497         workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.",
498                 MAPS_APP_NAME);
499     }
500 
501     @FlakyTest(bugId = 256615483)
502     @Test
503     @PortraitLandscape
testPressBack()504     public void testPressBack() throws Exception {
505         InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
506                 READ_DEVICE_CONFIG_PERMISSION);
507         assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
508         mLauncher.getWorkspace().switchToAllApps();
509         mLauncher.pressBack();
510         mLauncher.getWorkspace();
511         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
512         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
513         mLauncher.pressBack();
514         mLauncher.getWorkspace();
515         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
516     }
517 
518     @Test
519     @PortraitLandscape
520     @PlatinumTest(focusArea = "launcher")
testDragAndCancelAppIcon()521     public void testDragAndCancelAppIcon() {
522         final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
523         Point positionBeforeDrag =
524                 mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
525         assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag);
526 
527         mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon);
528 
529         Point positionAfterDrag =
530                 mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
531         assertNotNull("App not found in Workspace after dragging.", positionAfterDrag);
532         assertEquals("App not returned to same position in Workspace after drag & cancel",
533                 positionBeforeDrag, positionAfterDrag);
534     }
535 
536     @Test
537     @PortraitLandscape
testDeleteFromWorkspace()538     public void testDeleteFromWorkspace() throws Exception {
539         // test delete both built-in apps and user-installed app from workspace
540         for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
541             final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
542             Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon);
543             workspace.verifyWorkspaceAppIconIsGone(
544                     appName + " app was found after being deleted from workspace",
545                     appName);
546         }
547     }
548 
verifyAppUninstalledFromAllApps(Workspace workspace, String appName)549     private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
550         final HomeAllApps allApps = workspace.switchToAllApps();
551         Wait.atMost(appName + " app was found on all apps after being uninstalled",
552                 () -> allApps.tryGetAppIcon(appName) == null,
553                 DEFAULT_UI_TIMEOUT, mLauncher);
554     }
555 
556     @Test
557     @PortraitLandscape
558     // TODO(b/293944634): Remove Screenrecord after flaky debug, and add
559     // @PlatinumTest(focusArea = "launcher") back
560     @ScreenRecord
testUninstallFromWorkspace()561     public void testUninstallFromWorkspace() throws Exception {
562         installDummyAppAndWaitForUIUpdate();
563         try {
564             verifyAppUninstalledFromAllApps(
565                     createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
566         } finally {
567             TestUtil.uninstallDummyApp();
568         }
569     }
570 
571     @Test
572     @PortraitLandscape
573     @PlatinumTest(focusArea = "launcher")
testUninstallFromAllApps()574     public void testUninstallFromAllApps() throws Exception {
575         installDummyAppAndWaitForUIUpdate();
576         try {
577             Workspace workspace = mLauncher.getWorkspace();
578             final HomeAllApps allApps = workspace.switchToAllApps();
579             workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
580             verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME);
581         } finally {
582             TestUtil.uninstallDummyApp();
583         }
584     }
585 
586     @Test
587     @PortraitLandscape
588     @PlatinumTest(focusArea = "launcher")
testDragAppIconToWorkspaceCell()589     public void testDragAppIconToWorkspaceCell() throws Exception {
590         long startTime, endTime, elapsedTime;
591         Point[] targets = getCornersAndCenterPositions();
592 
593         for (Point target : targets) {
594             startTime = SystemClock.uptimeMillis();
595             final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
596             allApps.freeze();
597             try {
598                 allApps.getAppIcon(APP_NAME).dragToWorkspace(target.x, target.y);
599             } finally {
600                 allApps.unfreeze();
601             }
602             // Reset the workspace for the next shortcut creation.
603             initialize(this, true);
604             endTime = SystemClock.uptimeMillis();
605             elapsedTime = endTime - startTime;
606             Log.d("testDragAppIconToWorkspaceCellTime",
607                     "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime);
608         }
609 
610         // test to move a shortcut to other cell.
611         final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(APP_NAME);
612         for (Point target : targets) {
613             startTime = SystemClock.uptimeMillis();
614             launcherTestAppIcon.dragToWorkspace(target.x, target.y);
615             endTime = SystemClock.uptimeMillis();
616             elapsedTime = endTime - startTime;
617             Log.d("testDragAppIconToWorkspaceCellTime",
618                     "Milliseconds taken to move shortcut to other cell: " + elapsedTime);
619         }
620     }
621 
622     /**
623      * Adds three icons to the workspace and removes one of them by dragging to uninstall.
624      */
625     @Test
626     @ScreenRecord // b/241821721
627     @PlatinumTest(focusArea = "launcher")
uninstallWorkspaceIcon()628     public void uninstallWorkspaceIcon() throws IOException {
629         Point[] gridPositions = getCornersAndCenterPositions();
630         StringBuilder sb = new StringBuilder();
631         for (Point p : gridPositions) {
632             sb.append(p).append(", ");
633         }
634         Log.d(ICON_MISSING, "allGridPositions: " + sb);
635         createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]);
636         createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]);
637         installDummyAppAndWaitForUIUpdate();
638         try {
639             createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]);
640             Map<String, Point> initialPositions =
641                     mLauncher.getWorkspace().getWorkspaceIconsPositions();
642             assertThat(initialPositions.keySet())
643                     .containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME);
644 
645             mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
646             mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
647                     DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
648 
649             // Debug for b/288944469 I want to test if we are not waiting enough after removing
650             // the icon to request the list of icons again, since the items are not removed
651             // immediately. This should reduce the flake rate
652             SystemClock.sleep(500);
653             Map<String, Point> finalPositions =
654                     mLauncher.getWorkspace().getWorkspaceIconsPositions();
655             assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
656         } finally {
657             TestUtil.uninstallDummyApp();
658         }
659     }
660 
661     @Test
662     @PortraitLandscape
663     @PlatinumTest(focusArea = "launcher")
testDragShortcutToWorkspaceCell()664     public void testDragShortcutToWorkspaceCell() throws Exception {
665         Point[] targets = getCornersAndCenterPositions();
666 
667         for (Point target : targets) {
668             final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
669             allApps.freeze();
670             try {
671                 allApps.getAppIcon(APP_NAME)
672                         .openDeepShortcutMenu()
673                         .getMenuItem(0)
674                         .dragToWorkspace(target.x, target.y);
675             } finally {
676                 allApps.unfreeze();
677             }
678         }
679     }
680 
681     @Test
682     @PortraitLandscape
testAddDeleteShortcutOnHotseat()683     public void testAddDeleteShortcutOnHotseat() {
684         mLauncher.getWorkspace()
685                 .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
686                 .switchToAllApps()
687                 .getAppIcon(APP_NAME)
688                 .dragToHotseat(0);
689         mLauncher.getWorkspace().deleteAppIcon(
690                 mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
691     }
692 
installDummyAppAndWaitForUIUpdate()693     private void installDummyAppAndWaitForUIUpdate() throws IOException {
694         TestUtil.installDummyApp();
695         waitForLauncherUIUpdate();
696     }
697 
waitForLauncherUIUpdate()698     private void waitForLauncherUIUpdate() {
699         // Wait for model thread completion as it may be processing
700         // the install event from the SystemService
701         mLauncher.waitForModelQueueCleared();
702         // Wait for Launcher UI thread completion, as it may be processing updating the UI in
703         // response to the model update. Not that `waitForLauncherInitialized` is just a proxy
704         // method, we can use any method which touches Launcher UI thread,
705         mLauncher.waitForLauncherInitialized();
706     }
707 
708     /**
709      * @return List of workspace grid coordinates. Those are not pixels. See {@link
710      * Workspace#getIconGridDimensions()}
711      */
getCornersAndCenterPositions()712     private Point[] getCornersAndCenterPositions() {
713         final Point dimensions = mLauncher.getWorkspace().getIconGridDimensions();
714         return new Point[]{
715                 new Point(0, 1),
716                 new Point(0, dimensions.y - 2),
717                 new Point(dimensions.x - 1, 1),
718                 new Point(dimensions.x - 1, dimensions.y - 2),
719                 new Point(dimensions.x / 2, dimensions.y / 2)
720         };
721     }
722 
getAppPackageName()723     public static String getAppPackageName() {
724         return getInstrumentation().getContext().getPackageName();
725     }
726 
727     @Test
testGetAppIconName()728     public void testGetAppIconName() {
729         HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
730         allApps.freeze();
731         try {
732             // getAppIcon() already verifies that the icon is not null and is the correct icon name.
733             allApps.getAppIcon(APP_NAME);
734         } finally {
735             allApps.unfreeze();
736         }
737     }
738 }
739