1 /* 2 * Copyright (C) 2023 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 package com.android.launcher3.dragging; 17 18 import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; 19 import static com.android.launcher3.testing.shared.TestProtocol.UIOBJECT_STALE_ELEMENT; 20 import static com.android.launcher3.util.TestConstants.AppNames.DUMMY_APP_NAME; 21 import static com.android.launcher3.util.TestConstants.AppNames.GMAIL_APP_NAME; 22 import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME; 23 import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME; 24 import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; 25 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; 26 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; 27 28 import static com.google.common.truth.Truth.assertThat; 29 30 import android.graphics.Point; 31 import android.platform.test.annotations.PlatinumTest; 32 import android.util.Log; 33 34 import com.android.launcher3.Launcher; 35 import com.android.launcher3.tapl.HomeAllApps; 36 import com.android.launcher3.tapl.HomeAppIcon; 37 import com.android.launcher3.tapl.Workspace; 38 import com.android.launcher3.ui.AbstractLauncherUiTest; 39 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; 40 import com.android.launcher3.util.TestUtil; 41 import com.android.launcher3.util.Wait; 42 import com.android.launcher3.util.rule.ScreenRecordRule; 43 import com.android.launcher3.util.rule.TestStabilityRule; 44 45 import org.junit.Test; 46 47 import java.io.IOException; 48 import java.util.Arrays; 49 import java.util.Map; 50 51 /** 52 * Test runs in Out of process (Oop) and In process (Ipc) 53 * Test the behaviour of uninstalling and removing apps both from AllApps, Workspace and Hotseat. 54 */ 55 public class TaplUninstallRemoveTest extends AbstractLauncherUiTest<Launcher> { 56 57 /** 58 * Deletes app both built-in and user-installed from the Workspace and makes sure it's no longer 59 * in the Workspace. 60 */ 61 @Test 62 @PortraitLandscape testDeleteFromWorkspace()63 public void testDeleteFromWorkspace() { 64 for (String appName : new String[]{GMAIL_APP_NAME, STORE_APP_NAME, TEST_APP_NAME}) { 65 final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName); 66 Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon); 67 workspace.verifyWorkspaceAppIconIsGone( 68 appName + " app was found after being deleted from workspace", 69 appName); 70 } 71 } 72 verifyAppUninstalledFromAllApps(Workspace workspace, String appName)73 private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) { 74 final HomeAllApps allApps = workspace.switchToAllApps(); 75 Wait.atMost(appName + " app was found on all apps after being uninstalled", 76 () -> allApps.tryGetAppIcon(appName) == null, 77 DEFAULT_UI_TIMEOUT, mLauncher); 78 } 79 installDummyAppAndWaitForUIUpdate()80 private void installDummyAppAndWaitForUIUpdate() throws IOException { 81 TestUtil.installDummyApp(); 82 waitForLauncherUIUpdate(); 83 } 84 waitForLauncherUIUpdate()85 private void waitForLauncherUIUpdate() { 86 // Wait for model thread completion as it may be processing 87 // the install event from the SystemService 88 mLauncher.waitForModelQueueCleared(); 89 // Wait for Launcher UI thread completion, as it may be processing updating the UI in 90 // response to the model update. Not that `waitForLauncherInitialized` is just a proxy 91 // method, we can use any method which touches Launcher UI thread, 92 mLauncher.waitForLauncherInitialized(); 93 } 94 95 /** 96 * Makes sure you can uninstall an app from the Workspace. 97 */ 98 @Test 99 @PortraitLandscape 100 @PlatinumTest(focusArea = "launcher") 101 @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/311099513 testUninstallFromWorkspace()102 public void testUninstallFromWorkspace() throws Exception { 103 installDummyAppAndWaitForUIUpdate(); 104 try { 105 verifyAppUninstalledFromAllApps( 106 createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); 107 } finally { 108 TestUtil.uninstallDummyApp(); 109 } 110 } 111 112 /** 113 * Makes sure you can uninstall an app from AllApps. 114 */ 115 @Test 116 @PortraitLandscape 117 @PlatinumTest(focusArea = "launcher") testUninstallFromAllApps()118 public void testUninstallFromAllApps() throws Exception { 119 installDummyAppAndWaitForUIUpdate(); 120 try { 121 Workspace workspace = mLauncher.getWorkspace(); 122 final HomeAllApps allApps = workspace.switchToAllApps(); 123 workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall(); 124 verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME); 125 } finally { 126 TestUtil.uninstallDummyApp(); 127 } 128 } 129 130 /** 131 * Adds three icons to the workspace and removes one of them by dragging to uninstall. 132 */ 133 @Test 134 @PlatinumTest(focusArea = "launcher") 135 @ScreenRecordRule.ScreenRecord // b/319501259 uninstallWorkspaceIcon()136 public void uninstallWorkspaceIcon() throws IOException { 137 Point[] gridPositions = TestUtil.getCornersAndCenterPositions(mLauncher); 138 StringBuilder sb = new StringBuilder(); 139 for (Point p : gridPositions) { 140 sb.append(p).append(", "); 141 } 142 Log.d(ICON_MISSING, "allGridPositions: " + sb); 143 try { 144 installDummyAppAndWaitForUIUpdate(); 145 146 final String[] appNameCandidates = {DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME}; 147 148 // List of test apps trimmed down to the length of grid positions. 149 final String[] appNames = Arrays.copyOfRange( 150 appNameCandidates, 151 0, Math.min(gridPositions.length, appNameCandidates.length)); 152 153 for (int i = 0; i < appNames.length; ++i) { 154 Log.d(UIOBJECT_STALE_ELEMENT, "creatingShortcut for: " + appNames[i]); 155 createShortcutIfNotExist(appNames[i], gridPositions[i]); 156 } 157 158 Map<String, Point> initialPositions = 159 mLauncher.getWorkspace().getWorkspaceIconsPositions(); 160 assertThat(initialPositions.keySet()).containsAtLeastElementsIn(appNames); 161 162 mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall(); 163 mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone( 164 DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME); 165 166 Log.d(UIOBJECT_STALE_ELEMENT, "second getWorkspaceIconsPositions()"); 167 Map<String, Point> finalPositions = 168 mLauncher.getWorkspace().getWorkspaceIconsPositions(); 169 assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME); 170 } finally { 171 TestUtil.uninstallDummyApp(); 172 } 173 } 174 175 /** 176 * Drag icon from the Hotseat to the delete drop target 177 */ 178 @Test 179 @PortraitLandscape 180 @ScreenRecordRule.ScreenRecord // b/338869019 181 @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/338869019 testAddDeleteShortcutOnHotseat()182 public void testAddDeleteShortcutOnHotseat() { 183 mLauncher.getWorkspace() 184 .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0)) 185 .switchToAllApps() 186 .getAppIcon(TEST_APP_NAME) 187 .dragToHotseat(0); 188 mLauncher.getWorkspace().deleteAppIcon( 189 mLauncher.getWorkspace().getHotseatAppIcon(TEST_APP_NAME)); 190 } 191 } 192