diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index 97052b29c5..dd5812302d 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -41,11 +41,10 @@ public class AccessibilityManagerCompat { } /** - * * @param target The view the accessibility event is initialized on. * If null, this method has no effect. - * @param type See TYPE_ constants defined in {@link AccessibilityEvent}. - * @param text Optional text to add to the event, which will be announced to the user. + * @param type See TYPE_ constants defined in {@link AccessibilityEvent}. + * @param text Optional text to add to the event, which will be announced to the user. */ public static void sendCustomAccessibilityEvent(@Nullable View target, int type, @Nullable String text) { @@ -97,6 +96,16 @@ public class AccessibilityManagerCompat { null); } + /** + * Notify running tests of a folder opened. + */ + public static void sendFolderOpenedEventToTest(Context context) { + final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); + if (accessibilityManager == null) return; + + sendEventToTest(accessibilityManager, context, TestProtocol.FOLDER_OPENED_MESSAGE, null); + } + private static void sendEventToTest( AccessibilityManager accessibilityManager, Context context, String eventTag, Bundle data) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 879739f171..daef68226d 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -78,6 +78,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.anim.KeyboardInsetAnimationCallback; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; @@ -687,6 +688,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public void onAnimationEnd(Animator animation) { mState = STATE_OPEN; announceAccessibilityChanges(); + AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext()); mContent.setFocusOnFirstChild(); } @@ -1265,7 +1267,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo ? (PendingAddShortcutInfo) d.dragInfo : null; - WorkspaceItemInfo pasiSi = pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null; + WorkspaceItemInfo pasiSi = + pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null; if (pasi != null && pasiSi == null) { // There is no WorkspaceItemInfo, so we have to go through a configuration activity. pasi.container = mInfo.id; diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 5a9c074f96..17d925c1f9 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -21,6 +21,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; +import android.content.res.Resources; import android.graphics.Insets; import android.os.Build; import android.os.Bundle; @@ -148,6 +149,14 @@ public class TestInformationHandler implements ResourceBasedOverride { TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest); return response; + case TestProtocol.REQUEST_START_DRAG_THRESHOLD: { + final Resources resources = mContext.getResources(); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, + resources.getDimensionPixelSize(R.dimen.deep_shortcuts_start_drag_threshold) + + resources.getDimensionPixelSize(R.dimen.pre_drag_view_scale)); + return response; + } + default: return null; } @@ -193,6 +202,7 @@ public class TestInformationHandler implements ResourceBasedOverride { /** * Generic interface for setting a fiend in bundle + * * @param the type of value being set */ public interface BundleSetter { diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 8cb7f539fc..db289021a5 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -25,6 +25,7 @@ public final class TestProtocol { public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED"; public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED"; public static final String DISMISS_ANIMATION_ENDS_MESSAGE = "TAPL_DISMISS_ANIMATION_ENDS"; + public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED"; public static final int NORMAL_STATE_ORDINAL = 0; public static final int SPRING_LOADED_STATE_ORDINAL = 1; public static final int OVERVIEW_STATE_ORDINAL = 2; @@ -99,6 +100,7 @@ public final class TestProtocol { public static final String REQUEST_CLEAR_DATA = "clear-data"; public static final String REQUEST_IS_TABLET = "is-tablet"; public static final String REQUEST_IS_TWO_PANELS = "is-two-panel"; + public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold"; public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT = "get-activities-created-count"; public static final String REQUEST_GET_ACTIVITIES = "get-activities"; diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 881f50cafb..2fa84b2430 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -34,6 +34,8 @@ import com.android.launcher3.tapl.AllApps; import com.android.launcher3.tapl.AppIcon; import com.android.launcher3.tapl.AppIconMenu; import com.android.launcher3.tapl.AppIconMenuItem; +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.views.OptionsPopupView; @@ -369,6 +371,48 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + private AppIcon createShortcutIfNotExist(String name) { + AppIcon appIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name); + if (appIcon == null) { + AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + appIcon = allApps.getAppIcon(name); + appIcon.dragToWorkspace(false, false); + } finally { + allApps.unfreeze(); + } + appIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name); + } + return appIcon; + } + + @Test + @PortraitLandscape + public void testDragToFolder() throws Exception { + final AppIcon playStoreIcon = createShortcutIfNotExist("Play Store"); + final AppIcon gmailIcon = createShortcutIfNotExist("Gmail"); + + FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon); + + Folder folder = folderIcon.open(); + folder.getAppIcon("Play Store"); + folder.getAppIcon("Gmail"); + Workspace workspace = folder.close(); + + assertNull("Gmail should be moved to a folder.", + workspace.tryGetWorkspaceAppIcon("Gmail")); + assertNull("Play Store should be moved to a folder.", + workspace.tryGetWorkspaceAppIcon("Play Store")); + + final AppIcon youTubeIcon = createShortcutIfNotExist("YouTube"); + + folderIcon = youTubeIcon.dragToIcon(folderIcon); + folder = folderIcon.open(); + folder.getAppIcon("YouTube"); + folder.close(); + } + public static String getAppPackageName() { return getInstrumentation().getContext().getPackageName(); } diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index 21099b4934..6da59da66c 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -16,8 +16,11 @@ package com.android.launcher3.tapl; +import android.graphics.Point; +import android.graphics.Rect; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; @@ -29,7 +32,7 @@ import java.util.regex.Pattern; /** * App icon, whether in all apps or in workspace/ */ -public final class AppIcon extends Launchable { +public final class AppIcon extends Launchable implements FolderDragTarget { private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick"); @@ -61,6 +64,29 @@ public final class AppIcon extends Launchable { } } + /** + * Drag the AppIcon to the given position of other icon. The drag must result in a folder. + * + * @param target the destination icon. + */ + @NonNull + public FolderIcon dragToIcon(FolderDragTarget target) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to drag icon")) { + final Rect dropBounds = target.getDropLocationBounds(); + Workspace.dragIconToWorkspace( + mLauncher, this, + () -> { + final Rect bounds = target.getDropLocationBounds(); + return new Point(bounds.centerX(), bounds.centerY()); + }, + getLongPressIndicator()); + FolderIcon result = target.getTargetFolder(dropBounds); + mLauncher.assertTrue("Can't find the target folder.", result != null); + return result; + } + } + @Override protected void addExpectedEventsForLongClick() { mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT); @@ -80,4 +106,20 @@ public final class AppIcon extends Launchable { protected String launchableType() { return "app icon"; } + + @Override + public Rect getDropLocationBounds() { + return mLauncher.getVisibleBounds(mObject); + } + + @Override + public FolderIcon getTargetFolder(Rect bounds) { + for (FolderIcon folderIcon : mLauncher.getWorkspace().getFolderIcons()) { + final Rect folderIconBounds = folderIcon.getDropLocationBounds(); + if (bounds.contains(folderIconBounds.centerX(), folderIconBounds.centerY())) { + return folderIcon; + } + } + return null; + } } diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java new file mode 100644 index 0000000000..dba308dbf7 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Folder.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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 android.graphics.Point; +import android.graphics.Rect; +import android.os.SystemClock; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.test.uiautomator.UiObject2; + +public class Folder { + + protected static final String FOLDER_CONTENT_RES_ID = "folder_content"; + + private final UiObject2 mContainer; + private final LauncherInstrumentation mLauncher; + + Folder(LauncherInstrumentation launcher) { + this.mLauncher = launcher; + this.mContainer = launcher.waitForLauncherObject(FOLDER_CONTENT_RES_ID); + } + + /** + * Find an app icon with given name or raise assertion error. + */ + @NonNull + public AppIcon getAppIcon(String appName) { + try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer( + "Want to get app icon in folder")) { + return new AppIcon(mLauncher, + mLauncher.waitForObjectInContainer( + mContainer, + AppIcon.getAppIconSelector(appName, mLauncher))); + } + } + + private void touchOutsideFolder() { + Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer); + final long downTime = SystemClock.uptimeMillis(); + Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, + containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, + containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE); + } + + /** + * CLose opened folder if possible. It throws assertion error if the folder is already closed. + */ + public Workspace close() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "Want to close opened folder")) { + mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID); + touchOutsideFolder(); + mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID); + return mLauncher.getWorkspace(); + } + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java b/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java new file mode 100644 index 0000000000..d797418ab4 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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 android.graphics.Rect; + +public interface FolderDragTarget { + Rect getDropLocationBounds(); + + FolderIcon getTargetFolder(Rect bounds); +} diff --git a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java new file mode 100644 index 0000000000..2e79d70ee6 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 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 android.graphics.Rect; + +import androidx.annotation.NonNull; +import androidx.test.uiautomator.UiObject2; + +import com.android.launcher3.testing.TestProtocol; + +/** + * Folder Icon, an app folder in workspace. + */ +public class FolderIcon implements FolderDragTarget { + + protected final UiObject2 mObject; + protected final LauncherInstrumentation mLauncher; + + FolderIcon(LauncherInstrumentation launcher, UiObject2 icon) { + mObject = icon; + mLauncher = launcher; + } + + /** + * Open and return a folder or raise assertion error. + */ + @NonNull + public Folder open() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer("open folder")) { + mLauncher.executeAndWaitForLauncherEvent(() -> mLauncher.clickLauncherObject(mObject), + event -> TestProtocol.FOLDER_OPENED_MESSAGE.equals( + event.getClassName().toString()), + () -> "Fail to open folder.", + "open folder"); + } + return new Folder(mLauncher); + } + + @Override + public Rect getDropLocationBounds() { + return mLauncher.getVisibleBounds(mObject.getParent()); + } + + @Override + public FolderIcon getTargetFolder(Rect bounds) { + return this; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 7ffdf4cef8..3ac5fa5bbd 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.DONT_KILL_APP; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; +import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID; import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; @@ -80,6 +81,7 @@ import java.util.Collections; import java.util.Deque; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @@ -767,6 +769,47 @@ public final class LauncherInstrumentation { } } + /** + * Get the resource ID of visible floating view. + */ + private Optional getFloatingResId() { + if (hasLauncherObject(CONTEXT_MENU_RES_ID)) { + return Optional.of(CONTEXT_MENU_RES_ID); + } + if (hasLauncherObject(FOLDER_CONTENT_RES_ID)) { + return Optional.of(FOLDER_CONTENT_RES_ID); + } + return Optional.empty(); + } + + /** + * Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content. + */ + private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) { + final Point displaySize = getRealDisplaySize(); + + final Optional floatingRes = getFloatingResId(); + + if (!floatingRes.isPresent()) { + return; + } + + GestureScope gestureScope = gestureStartFromLauncher + ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE) + : GestureScope.OUTSIDE_WITH_PILFER; + linearGesture( + displaySize.x / 2, displaySize.y - 1, + displaySize.x / 2, 0, + ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, + false, gestureScope); + + try (LauncherInstrumentation.Closable c1 = addContextLayer( + String.format("Swiped up from floating view %s to home", floatingRes.get()))) { + waitUntilLauncherObjectGone(floatingRes.get()); + waitForLauncherObject(getAnyObjectSelector()); + } + } + /** * Presses nav bar home button. * @@ -791,21 +834,9 @@ public final class LauncherInstrumentation { ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID) : isLauncherVisible(); - if (hasLauncherObject(CONTEXT_MENU_RES_ID)) { - GestureScope gestureScope = gestureStartFromLauncher - ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE) - : GestureScope.OUTSIDE_WITH_PILFER; - linearGesture( - displaySize.x / 2, displaySize.y - 1, - displaySize.x / 2, 0, - ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, - false, gestureScope); - try (LauncherInstrumentation.Closable c1 = addContextLayer( - "Swiped up from context menu to home")) { - waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID); - waitForLauncherObject(getAnyObjectSelector()); - } - } + // CLose floating views before going back to home. + swipeUpToCloseFloatingView(gestureStartFromLauncher); + if (hasLauncherObject(WORKSPACE_RES_ID)) { log(action = "already at home"); } else { diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 288c85319b..0145690909 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -36,7 +36,10 @@ import androidx.test.uiautomator.UiObject2; import com.android.launcher3.testing.TestProtocol; +import java.util.List; +import java.util.function.Supplier; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Operations on the workspace screen. @@ -170,40 +173,100 @@ public final class Workspace extends Home { mHotseat, AppIcon.getAppIconSelector(appName, mLauncher))); } - static void dragIconToWorkspace( - LauncherInstrumentation launcher, Launchable launchable, Point dest, - String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut, - Runnable expectLongClickEvents) { - LauncherInstrumentation.log("dragIconToWorkspace: begin"); - final Point launchableCenter = launchable.getObject().getVisibleCenter(); - final long downTime = SystemClock.uptimeMillis(); - launcher.runToState( - () -> { - launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, - launchableCenter, LauncherInstrumentation.GestureScope.INSIDE); - LauncherInstrumentation.log("dragIconToWorkspace: sent down"); - expectLongClickEvents.run(); - launcher.waitForLauncherObject(longPressIndicator); - LauncherInstrumentation.log("dragIconToWorkspace: indicator"); - launcher.movePointer(launchableCenter, dest, 10, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE); - }, - SPRING_LOADED_STATE_ORDINAL, - "long-pressing and moving"); - LauncherInstrumentation.log("dragIconToWorkspace: moved pointer"); + private static int getStartDragThreshold(LauncherInstrumentation launcher) { + return launcher.getTestInfo(TestProtocol.REQUEST_START_DRAG_THRESHOLD).getInt( + TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + + /** + * Finds folder icons in the current workspace. + * + * @return a list of folder icons. + */ + List getFolderIcons() { + final UiObject2 workspace = verifyActiveContainer(); + return mLauncher.getObjectsInContainer(workspace, "folder_icon_name").stream().map( + o -> new FolderIcon(mLauncher, o)).collect(Collectors.toList()); + } + + /** + * Drag an icon up with a short distance that makes workspace go to spring loaded state. + * + * @return the position after dragging. + */ + private static Point dragIconToSpringLoaded(LauncherInstrumentation launcher, long downTime, + UiObject2 icon, + String longPressIndicator, Runnable expectLongClickEvents) { + final Point iconCenter = icon.getVisibleCenter(); + final Point dragStartCenter = new Point(iconCenter.x, + iconCenter.y - getStartDragThreshold(launcher)); + + launcher.runToState(() -> { + launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, + iconCenter, LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.log("dragIconToSpringLoaded: sent down"); + expectLongClickEvents.run(); + launcher.waitForLauncherObject(longPressIndicator); + LauncherInstrumentation.log("dragIconToSpringLoaded: indicator"); + launcher.movePointer(iconCenter, dragStartCenter, 10, downTime, true, + LauncherInstrumentation.GestureScope.INSIDE); + }, SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start"); + return dragStartCenter; + } + + private static void dropDraggedIcon(LauncherInstrumentation launcher, Point dest, long downTime, + @Nullable Runnable expectedEvents) { launcher.runToState( () -> launcher.sendPointer( downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest, LauncherInstrumentation.GestureScope.INSIDE), NORMAL_STATE_ORDINAL, "sending UP event"); - if (startsActivity || isWidgetShortcut) { - launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START); + if (expectedEvents != null) { + expectedEvents.run(); } - LauncherInstrumentation.log("dragIconToWorkspace: end"); + LauncherInstrumentation.log("dropIcon: end"); launcher.waitUntilLauncherObjectGone("drop_target_bar"); } + static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable, + Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut, + Runnable expectLongClickEvents) { + Runnable expectDropEvents = null; + if (startsActivity || isWidgetShortcut) { + expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, + LauncherInstrumentation.EVENT_START); + } + dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator, + expectLongClickEvents, expectDropEvents); + } + + /** + * Drag icon in workspace to else where. + * This function expects the launchable is inside the workspace and there is no drop event. + */ + static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable, + Supplier destSupplier, String longPressIndicator) { + dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator, + () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null); + } + + static void dragIconToWorkspace( + LauncherInstrumentation launcher, Launchable launchable, Supplier dest, + String longPressIndicator, Runnable expectLongClickEvents, + @Nullable Runnable expectDropEvents) { + try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer( + "want to drag icon to workspace")) { + final long downTime = SystemClock.uptimeMillis(); + final Point dragStartCenter = dragIconToSpringLoaded(launcher, downTime, + launchable.getObject(), longPressIndicator, expectLongClickEvents); + final Point targetDest = dest.get(); + launcher.movePointer(dragStartCenter, targetDest, 10, downTime, true, + LauncherInstrumentation.GestureScope.INSIDE); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + } + } + /** * Flings to get to screens on the right. Waits for scrolling and a possible overscroll * recoil to complete.