diff --git a/tests/Android.bp b/tests/Android.bp index 3670c37add..8def20fdc9 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -32,6 +32,7 @@ filegroup { srcs: [ "src/com/android/launcher3/ui/AbstractLauncherUiTest.java", "src/com/android/launcher3/ui/PortraitLandscapeRunner.java", + "src/com/android/launcher3/util/TestUtil.java", "src/com/android/launcher3/util/Wait.java", "src/com/android/launcher3/util/WidgetUtils.java", "src/com/android/launcher3/util/rule/FailureWatcher.java", @@ -71,6 +72,11 @@ android_library { platform_apis: true, } +android_library { + name: "Launcher3TestResources", + resource_dirs: ["res"], +} + android_test { name: "Launcher3Tests", srcs: [ diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 5b940a88d8..f193e24be8 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -38,6 +38,7 @@ import com.android.launcher3.tapl.Folder; import com.android.launcher3.tapl.FolderIcon; import com.android.launcher3.tapl.Widgets; import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.util.TestUtil; import com.android.launcher3.widget.picker.WidgetsFullSheet; import com.android.launcher3.widget.picker.WidgetsRecyclerView; @@ -50,6 +51,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class TaplTestsLauncher3 extends AbstractLauncherUiTest { private static final String APP_NAME = "LauncherTestApp"; + private static final String DUMMY_APP_NAME = "Aardwolf"; @Before public void setUp() throws Exception { @@ -435,7 +437,49 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) { + final AllApps allApps = workspace.switchToAllApps(); + allApps.freeze(); + try { + assertNull(appName + " app was found on all apps after being uninstalled", + allApps.tryGetAppIcon(appName)); + } finally { + allApps.unfreeze(); + } + } + + @Test + @PortraitLandscape + public void testUninstallFromWorkspace() throws Exception { + TestUtil.installDummyApp(); + try { + verifyAppUninstalledFromAllApps( + createShortcutIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); + } finally { + TestUtil.uninstallDummyApp(); + } + } + + @Test + @PortraitLandscape + public void testUninstallFromAllApps() throws Exception { + TestUtil.installDummyApp(); + try { + Workspace workspace = mLauncher.getWorkspace(); + final AllApps allApps = workspace.switchToAllApps(); + allApps.freeze(); + try { + workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall(); + } finally { + allApps.unfreeze(); + } + verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME); + } finally { + TestUtil.uninstallDummyApp(); + } + } + public static String getAppPackageName() { return getInstrumentation().getContext().getPackageName(); } -} +} \ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java index 0582bc9557..98eb32e818 100644 --- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java +++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java @@ -22,8 +22,6 @@ import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; -import com.android.launcher3.testing.TestProtocol; - import java.util.regex.Pattern; public class AddToHomeScreenPrompt { @@ -44,19 +42,10 @@ public class AddToHomeScreenPrompt { public void addAutomatically() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { - if (mLauncher.getNavigationModel() - != LauncherInstrumentation.NavigationModel.THREE_BUTTON) { - if (!mLauncher.isLauncher3()) { - mLauncher.expectEvent( - TestProtocol.SEQUENCE_TIS, - LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS); - mLauncher.expectEvent( - TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS); - } - } - mLauncher.waitForObjectInContainer( - mWidgetCell.getParent().getParent().getParent().getParent(), - By.text(ADD_AUTOMATICALLY)).click(); + mLauncher.clickObject( + mLauncher.waitForObjectInContainer( + mWidgetCell.getParent().getParent().getParent().getParent(), + By.text(ADD_AUTOMATICALLY))); mLauncher.waitUntilLauncherObjectGone(getSelector()); } } diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 78301e48cd..c1b022017a 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; @@ -51,8 +52,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class)); verifyNotFrozen("All apps freeze flags upon opening all apps"); mIconHeight = mLauncher.getTestInfo( - TestProtocol.REQUEST_ICON_HEIGHT). - getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + TestProtocol.REQUEST_ICON_HEIGHT) + .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } @Override @@ -98,14 +99,14 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { } /** - * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make - * sure the icon is visible. + * Finds an icon. If the icon doesn't exist, return null. + * Scrolls the app list when needed to make sure the icon is visible. * * @param appName name of the app. - * @return The app. + * @return The app if found, and null if not found. */ - @NonNull - public AppIcon getAppIcon(String appName) { + @Nullable + public AppIcon tryGetAppIcon(String appName) { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "getting app icon " + appName + " on all apps")) { @@ -150,19 +151,34 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { } verifyActiveContainer(); } - // Ignore bottom offset selection here as there might not be any scroll more scroll // region available. - mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName, - hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector, - deviceHeight)); + if (hasClickableIcon( + allAppsContainer, appListRecycler, appIconSelector, deviceHeight)) { - final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler, - appIconSelector); - return new AppIcon(mLauncher, appIcon); + final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler, + appIconSelector); + return new AllAppsAppIcon(mLauncher, appIcon); + } else { + return null; + } } } + /** + * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make + * sure the icon is visible. + * + * @param appName name of the app. + * @return The app. + */ + @NonNull + public AppIcon getAppIcon(String appName) { + AppIcon appIcon = tryGetAppIcon(appName); + mLauncher.assertNotNull("Unable to scroll to a clickable icon: " + appName, appIcon); + return appIcon; + } + private void scrollBackToBeginning() { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to scroll back in all apps")) { @@ -195,7 +211,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { private int getAllAppsScroll() { return mLauncher.getTestInfo( - TestProtocol.REQUEST_APPS_LIST_SCROLL_Y) + TestProtocol.REQUEST_APPS_LIST_SCROLL_Y) .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } @@ -253,4 +269,4 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { if (testInfo == null) return; mLauncher.assertEquals(message, 0, testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD)); } -} +} \ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java b/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java new file mode 100644 index 0000000000..03d387e806 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +import java.util.regex.Pattern; + +/** + * App icon in all apps. + */ +final class AllAppsAppIcon extends AppIcon { + + private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick"); + + AllAppsAppIcon(LauncherInstrumentation launcher, UiObject2 icon) { + super(launcher, icon); + } + + @Override + protected Pattern getLongClickEvent() { + return LONG_CLICK_EVENT; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index 6da59da66c..8fa9e12e8e 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -32,9 +32,7 @@ import java.util.regex.Pattern; /** * App icon, whether in all apps or in workspace/ */ -public final class AppIcon extends Launchable implements FolderDragTarget { - - private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick"); +public abstract class AppIcon extends Launchable implements FolderDragTarget { AppIcon(LauncherInstrumentation launcher, UiObject2 icon) { super(launcher, icon); @@ -44,13 +42,15 @@ public final class AppIcon extends Launchable implements FolderDragTarget { return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName()); } + protected abstract Pattern getLongClickEvent(); + /** * Long-clicks the icon to open its menu. */ public AppIconMenu openMenu() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { return new AppIconMenu(mLauncher, mLauncher.clickAndGet( - mObject, "popup_container", LONG_CLICK_EVENT)); + mObject, "popup_container", getLongClickEvent())); } } @@ -60,7 +60,7 @@ public final class AppIcon extends Launchable implements FolderDragTarget { public AppIconMenu openDeepShortcutMenu() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { return new AppIconMenu(mLauncher, mLauncher.clickAndGet( - mObject, "deep_shortcuts_container", LONG_CLICK_EVENT)); + mObject, "deep_shortcuts_container", getLongClickEvent())); } } @@ -89,7 +89,7 @@ public final class AppIcon extends Launchable implements FolderDragTarget { @Override protected void addExpectedEventsForLongClick() { - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT); + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, getLongClickEvent()); } @Override @@ -122,4 +122,20 @@ public final class AppIcon extends Launchable implements FolderDragTarget { } return null; } -} + + /** + * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar. + * + * @return validated workspace after the existing appIcon being uninstalled. + */ + public Workspace uninstall() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "uninstalling app icon")) { + return Workspace.uninstallAppIcon( + mLauncher, this, + this::addExpectedEventsForLongClick + ); + } + } +} \ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java index dba308dbf7..f046b6e69c 100644 --- a/tests/tapl/com/android/launcher3/tapl/Folder.java +++ b/tests/tapl/com/android/launcher3/tapl/Folder.java @@ -43,7 +43,7 @@ public class Folder { public AppIcon getAppIcon(String appName) { try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer( "Want to get app icon in folder")) { - return new AppIcon(mLauncher, + return new WorkspaceAppIcon(mLauncher, mLauncher.waitForObjectInContainer( mContainer, AppIcon.getAppIconSelector(appName, mLauncher))); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 37a205c859..b42f5c17b1 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1284,10 +1284,8 @@ public final class LauncherInstrumentation { return getRealDisplaySize().x - getWindowInsets().right - 1; } - void clickLauncherObject(UiObject2 object) { - waitForObjectEnabled(object, "clickLauncherObject"); - expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN); - expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP); + void clickObject(UiObject2 object) { + waitForObjectEnabled(object, "clickObject"); if (!isLauncher3() && getNavigationModel() != NavigationModel.THREE_BUTTON) { expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS); expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS); @@ -1295,6 +1293,12 @@ public final class LauncherInstrumentation { object.click(); } + void clickLauncherObject(UiObject2 object) { + expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN); + expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP); + clickObject(object); + } + void scrollToLastVisibleRow( UiObject2 container, Collection items, diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 3f0d7fdc00..c71c11f928 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -20,6 +20,7 @@ import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL; +import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertTrue; import android.graphics.Point; @@ -31,8 +32,11 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; +import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; import com.android.launcher3.testing.TestProtocol; @@ -49,6 +53,7 @@ public final class Workspace extends Home { private static final int DEFAULT_DRAG_STEPS = 10; private static final String DROP_BAR_RES_ID = "drop_target_bar"; private static final String DELETE_TARGET_TEXT_ID = "delete_target_text"; + private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text"; static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile( "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W" @@ -56,7 +61,7 @@ public final class Workspace extends Home { static final Pattern EVENT_CTRL_W_UP = Pattern.compile( "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_W" + ".*?metaState=META_CTRL_ON"); - private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick"); + static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick"); private final UiObject2 mHotseat; @@ -81,8 +86,8 @@ public final class Workspace extends Home { final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1; final int swipeHeight = mLauncher.getTestInfo( - TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT). - getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT) + .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); LauncherInstrumentation.log( "switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY + ", swipeHeight = " + swipeHeight + ", slop = " @@ -116,7 +121,7 @@ public final class Workspace extends Home { final UiObject2 workspace = verifyActiveContainer(); final UiObject2 icon = workspace.findObject( AppIcon.getAppIconSelector(appName, mLauncher)); - return icon != null ? new AppIcon(mLauncher, icon) : null; + return icon != null ? new WorkspaceAppIcon(mLauncher, icon) : null; } } @@ -131,7 +136,7 @@ public final class Workspace extends Home { public AppIcon getWorkspaceAppIcon(String appName) { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to get a workspace icon")) { - return new AppIcon(mLauncher, + return new WorkspaceAppIcon(mLauncher, mLauncher.waitForObjectInContainer( verifyActiveContainer(), AppIcon.getAppIconSelector(appName, mLauncher))); @@ -205,7 +210,7 @@ public final class Workspace extends Home { @NonNull public AppIcon getHotseatAppIcon(String appName) { - return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer( + return new WorkspaceAppIcon(mLauncher, mLauncher.waitForObjectInContainer( mHotseat, AppIcon.getAppIconSelector(appName, mLauncher))); } @@ -215,12 +220,13 @@ public final class Workspace extends Home { } /* - * Get the center point of the delete icon in the drop target bar. + * Get the center point of the delete/uninstall icon in the drop target bar. */ - private Point getDeleteDropPoint() { - return mLauncher.waitForObjectInContainer( - mLauncher.waitForLauncherObject(DROP_BAR_RES_ID), - DELETE_TARGET_TEXT_ID).getVisibleCenter(); + private static Point getDropPointFromDropTargetBar( + LauncherInstrumentation launcher, String targetId) { + return launcher.waitForObjectInContainer( + launcher.waitForLauncherObject(DROP_BAR_RES_ID), + targetId).getVisibleCenter(); } /** @@ -235,7 +241,7 @@ public final class Workspace extends Home { "removing app icon from workspace")) { dragIconToWorkspace( mLauncher, appIcon, - () -> getDeleteDropPoint(), + () -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID), true, /* decelerating */ appIcon.getLongPressIndicator(), () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), @@ -248,6 +254,47 @@ public final class Workspace extends Home { } } + /** + * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar. + * + * @param launcher the root TAPL instrumentation object of {@link LauncherInstrumentation} type. + * @param appIcon to be uninstalled. + * @param expectLongClickEvents the runnable to be executed to verify expected longclick event. + * @return validated workspace after the existing appIcon being uninstalled. + */ + static Workspace uninstallAppIcon(LauncherInstrumentation launcher, AppIcon appIcon, + Runnable expectLongClickEvents) { + try (LauncherInstrumentation.Closable c = launcher.addContextLayer( + "uninstalling app icon")) { + dragIconToWorkspace( + launcher, appIcon, + () -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID), + true, /* decelerating */ + appIcon.getLongPressIndicator(), + expectLongClickEvents, + null); + + launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID); + + final BySelector installerAlert = By.text(Pattern.compile( + "Do you want to uninstall this app\\?", + Pattern.DOTALL | Pattern.MULTILINE)); + final UiDevice device = launcher.getDevice(); + assertTrue("uninstall alert is not shown", device.wait( + Until.hasObject(installerAlert), LauncherInstrumentation.WAIT_TIME_MS)); + final UiObject2 ok = device.findObject(By.text("OK")); + assertNotNull("OK button is not shown", ok); + launcher.clickObject(ok); + assertTrue("Uninstall alert is not dismissed after clicking OK", device.wait( + Until.gone(installerAlert), LauncherInstrumentation.WAIT_TIME_MS)); + + try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer( + "uninstalled app by dragging to the drop bar")) { + return new Workspace(launcher); + } + } + } + /** * Finds folder icons in the current workspace. * diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java new file mode 100644 index 0000000000..5f4e4695ef --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +import java.util.regex.Pattern; + +/** + * App icon in workspace. + */ +final class WorkspaceAppIcon extends AppIcon { + + WorkspaceAppIcon(LauncherInstrumentation launcher, UiObject2 icon) { + super(launcher, icon); + } + + @Override + protected Pattern getLongClickEvent() { + return Workspace.LONG_CLICK_EVENT; + } +}