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