diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 41c7c37032..5b940a88d8 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -423,6 +423,18 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); } + @Test + @PortraitLandscape + public void testDeleteFromWorkspace() throws Exception { + // test delete both built-in apps and user-installed app from workspace + for (String appName : new String[] {"Gmail", "Play Store", APP_NAME}) { + final AppIcon appIcon = createShortcutIfNotExist(appName); + Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(appIcon); + assertNull(appName + " app was found after being deleted from workspace", + workspace.tryGetWorkspaceAppIcon(appName)); + } + } + public static String getAppPackageName() { return getInstrumentation().getContext().getPackageName(); } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 91b1bc70c2..631e8f10c6 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1399,14 +1399,15 @@ public final class LauncherInstrumentation { final Point start = new Point(startX, startY); final Point end = new Point(endX, endY); sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope); - final long endTime = movePointer(start, end, steps, downTime, slowDown, gestureScope); + final long endTime = movePointer( + start, end, steps, false, downTime, slowDown, gestureScope); sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope); } - long movePointer(Point start, Point end, int steps, long downTime, boolean slowDown, - GestureScope gestureScope) { - long endTime = movePointer( - downTime, downTime, steps * GESTURE_STEP_MS, start, end, gestureScope); + long movePointer(Point start, Point end, int steps, boolean isDecelerating, + long downTime, boolean slowDown, GestureScope gestureScope) { + long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, + isDecelerating, start, end, gestureScope); if (slowDown) { endTime = movePointer(downTime, endTime + GESTURE_STEP_MS, 5 * GESTURE_STEP_MS, end, end, gestureScope); @@ -1485,21 +1486,55 @@ public final class LauncherInstrumentation { public long movePointer(long downTime, long startTime, long duration, Point from, Point to, GestureScope gestureScope) { + return movePointer( + downTime, startTime, duration, false, from, to, gestureScope); + } + + public long movePointer(long downTime, long startTime, long duration, boolean isDecelerating, + Point from, Point to, GestureScope gestureScope) { log("movePointer: " + from + " to " + to); final Point point = new Point(); long steps = duration / GESTURE_STEP_MS; + long currentTime = startTime; - for (long i = 0; i < steps; ++i) { - sleep(GESTURE_STEP_MS); - currentTime += GESTURE_STEP_MS; - final float progress = (currentTime - startTime) / (float) duration; + if (isDecelerating) { + // formula: V = V0 - D*T, assuming V = 0 when T = duration - point.x = from.x + (int) (progress * (to.x - from.x)); - point.y = from.y + (int) (progress * (to.y - from.y)); + // vx0: initial speed at the x-dimension, set as twice the avg speed + // dx: the constant deceleration at the x-dimension + double vx0 = 2 * (to.x - from.x) / duration; + double dx = vx0 / duration; + // vy0: initial speed at the y-dimension, set as twice the avg speed + // dy: the constant deceleration at the y-dimension + double vy0 = 2 * (to.y - from.y) / duration; + double dy = vy0 / duration; - sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope); + for (long i = 0; i < steps; ++i) { + sleep(GESTURE_STEP_MS); + currentTime += GESTURE_STEP_MS; + + // formula: P = P0 + V0*T - (D*T^2/2) + final double t = (i + 1) * GESTURE_STEP_MS; + point.x = from.x + (int) (vx0 * t - 0.5 * dx * t * t); + point.y = from.y + (int) (vy0 * t - 0.5 * dy * t * t); + + sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope); + } + } else { + for (long i = 0; i < steps; ++i) { + sleep(GESTURE_STEP_MS); + currentTime += GESTURE_STEP_MS; + + final float progress = (currentTime - startTime) / (float) duration; + point.x = from.x + (int) (progress * (to.x - from.x)); + point.y = from.y + (int) (progress * (to.y - from.y)); + + sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope); + + } } + return currentTime; } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index d9f5cc821d..3f0d7fdc00 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -46,6 +46,9 @@ import java.util.stream.Collectors; */ public final class Workspace extends Home { private static final int FLING_STEPS = 10; + 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"; static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile( "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W" @@ -211,6 +214,40 @@ public final class Workspace extends Home { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + /* + * Get the center point of the delete icon in the drop target bar. + */ + private Point getDeleteDropPoint() { + return mLauncher.waitForObjectInContainer( + mLauncher.waitForLauncherObject(DROP_BAR_RES_ID), + DELETE_TARGET_TEXT_ID).getVisibleCenter(); + } + + /** + * Delete the appIcon from the workspace. + * + * @param appIcon to be deleted. + * @return validated workspace after the existing appIcon being deleted. + */ + public Workspace deleteAppIcon(AppIcon appIcon) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "removing app icon from workspace")) { + dragIconToWorkspace( + mLauncher, appIcon, + () -> getDeleteDropPoint(), + true, /* decelerating */ + appIcon.getLongPressIndicator(), + () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), + null); + + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "dragged the app to the drop bar")) { + return new Workspace(mLauncher); + } + } + } + /** * Finds folder icons in the current workspace. * @@ -241,8 +278,8 @@ public final class Workspace extends Home { expectLongClickEvents.run(); launcher.waitForLauncherObject(longPressIndicator); LauncherInstrumentation.log("dragIconToSpringLoaded: indicator"); - launcher.movePointer(iconCenter, dragStartCenter, 10, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE); + launcher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false, + downTime, true, LauncherInstrumentation.GestureScope.INSIDE); }, SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start"); return dragStartCenter; } @@ -270,7 +307,7 @@ public final class Workspace extends Home { expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START); } - dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator, + dragIconToWorkspace(launcher, launchable, () -> dest, false, longPressIndicator, expectLongClickEvents, expectDropEvents); } @@ -280,13 +317,13 @@ public final class Workspace extends Home { */ static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable, Supplier destSupplier, String longPressIndicator) { - dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator, + dragIconToWorkspace(launcher, launchable, destSupplier, false, longPressIndicator, () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null); } static void dragIconToWorkspace( LauncherInstrumentation launcher, Launchable launchable, Supplier dest, - String longPressIndicator, Runnable expectLongClickEvents, + boolean isDecelerating, String longPressIndicator, Runnable expectLongClickEvents, @Nullable Runnable expectDropEvents) { try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer( "want to drag icon to workspace")) { @@ -301,8 +338,8 @@ public final class Workspace extends Home { while (targetDest.x > displayX || targetDest.x < 0) { int edgeX = targetDest.x > 0 ? displayX : 0; Point screenEdge = new Point(edgeX, targetDest.y); - launcher.movePointer(dragStart, screenEdge, 10, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE); + launcher.movePointer(dragStart, screenEdge, DEFAULT_DRAG_STEPS, isDecelerating, + downTime, true, LauncherInstrumentation.GestureScope.INSIDE); launcher.waitForIdle(); // Wait for the page change to happen targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1); dragStart = screenEdge; @@ -310,8 +347,8 @@ public final class Workspace extends Home { // targetDest.x is now between 0 and displayX so we found the target page, // we just have to put move the icon to the destination and drop it - launcher.movePointer(dragStart, targetDest, 10, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE); + launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating, + downTime, true, LauncherInstrumentation.GestureScope.INSIDE); dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); } }