diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java index 948f39e0fc..d3042cf82e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java @@ -19,7 +19,6 @@ package com.android.launcher3.appprediction; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS; -import android.app.ActivityManager; import android.content.Context; import android.graphics.CornerPathEffect; import android.graphics.Paint; @@ -39,6 +38,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.allapps.FloatingHeaderView; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.UserManagerCompat; @@ -152,7 +152,7 @@ public class AllAppsTipView extends AbstractFloatingView { || !launcher.isInState(ALL_APPS) || hasSeenAllAppsTip(launcher) || UserManagerCompat.getInstance(launcher).isDemoUser() - || ActivityManager.isRunningInTestHarness()) { + || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return false; } diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java index ecbaa5bda8..eefecda5d7 100644 --- a/src/com/android/launcher3/TestProtocol.java +++ b/src/com/android/launcher3/TestProtocol.java @@ -24,6 +24,7 @@ public final class TestProtocol { public static final String SCROLL_Y_FIELD = "scrollY"; public static final String STATE_FIELD = "state"; public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE"; + public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED"; public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE"; public static final int NORMAL_STATE_ORDINAL = 0; public static final int SPRING_LOADED_STATE_ORDINAL = 1; diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 180ca48166..548d5de0e0 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -32,7 +32,8 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.graphics.DrawableFactory; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -419,4 +420,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine public boolean hasOverlappingRendering() { return false; } + + @Override + public void onScrollStateChanged(int state) { + super.onScrollStateChanged(state); + + if (state == SCROLL_STATE_IDLE && Utilities.IS_RUNNING_IN_TEST_HARNESS) { + AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext()); + } + } } diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java index 7467119b5f..1d62b435d8 100644 --- a/src/com/android/launcher3/allapps/DiscoveryBounce.java +++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java @@ -24,7 +24,6 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerTyp import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; -import android.app.ActivityManager; import android.content.SharedPreferences; import android.os.Handler; import android.view.MotionEvent; @@ -32,6 +31,7 @@ import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.states.InternalStateHandler; @@ -134,7 +134,7 @@ public class DiscoveryBounce extends AbstractFloatingView { && !shouldShowForWorkProfile(launcher)) || AbstractFloatingView.getTopOpenView(launcher) != null || UserManagerCompat.getInstance(launcher).isDemoUser() - || ActivityManager.isRunningInTestHarness()) { + || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return; } @@ -159,7 +159,7 @@ public class DiscoveryBounce extends AbstractFloatingView { || (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false) && !shouldShowForWorkProfile(launcher)) || UserManagerCompat.getInstance(launcher).isDemoUser() - || ActivityManager.isRunningInTestHarness()) { + || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return; } diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index b4d0c54fc6..86f773fa31 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -62,6 +62,13 @@ public class AccessibilityManagerCompat { sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel); } + public static void sendScrollFinishedEventToTest(Context context) { + final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); + if (accessibilityManager == null) return; + + sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null); + } + private static void sendEventToTest( AccessibilityManager accessibilityManager, String eventTag, Bundle data) { final AccessibilityEvent e = AccessibilityEvent.obtain( diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 68bdfe3270..7ad7b74318 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -18,6 +18,8 @@ package com.android.launcher3.tapl; import static com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel.ZERO_BUTTON; +import android.graphics.Rect; + import androidx.annotation.NonNull; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; @@ -32,7 +34,6 @@ import com.android.launcher3.TestProtocol; public class AllApps extends LauncherInstrumentation.VisibleContainer { private static final int MAX_SCROLL_ATTEMPTS = 40; private static final int MIN_INTERACT_SIZE = 100; - private static final int FLING_SPEED = LauncherInstrumentation.needSlowGestures() ? 1000 : 3000; private final int mHeight; @@ -102,7 +103,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { "search_container_all_apps"); int attempts = 0; - allAppsContainer.setGestureMargins(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); + final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); for (int scroll = getScroll(allAppsContainer); scroll != 0; @@ -113,7 +114,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS, ++attempts <= MAX_SCROLL_ATTEMPTS); - allAppsContainer.scroll(Direction.UP, 1); + mLauncher.scrollWithModelTime(allAppsContainer, Direction.UP, 1, margins, 50); } try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) { @@ -133,7 +134,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { // Try to figure out how much percentage of the container needs to be scrolled in order // to reveal the app icon to have the MIN_INTERACT_SIZE final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f); - allAppsContainer.scroll(Direction.DOWN, pct); + mLauncher.scrollWithModelTime(allAppsContainer, Direction.DOWN, pct, null, 10); try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "scrolled an icon in all apps to make it visible - and then")) { mLauncher.waitForIdle(); @@ -150,9 +151,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.addContextLayer("want to fling forward in all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); // Start the gesture in the center to avoid starting at elements near the top. - allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2); - allAppsContainer.fling(Direction.DOWN, - (int) (FLING_SPEED * mLauncher.getDisplayDensity())); + mLauncher.scrollWithModelTime( + allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10); verifyActiveContainer(); } } @@ -165,9 +165,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.addContextLayer("want to fling backward in all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); // Start the gesture in the center, for symmetry with forward. - allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0); - allAppsContainer.fling(Direction.UP, - (int) (FLING_SPEED * mLauncher.getDisplayDensity())); + mLauncher.scrollWithModelTime( + allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10); verifyActiveContainer(); } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 4d8ff1bb2d..e5d27add2e 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -46,6 +47,7 @@ import androidx.annotation.Nullable; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Configurator; +import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; @@ -360,7 +362,7 @@ public final class LauncherInstrumentation { ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL; final Point displaySize = getRealDisplaySize(); - swipeViaMovePointer( + swipeWithModelTime( displaySize.x / 2, displaySize.y - 1, displaySize.x / 2, 0, finalState, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME); @@ -548,18 +550,65 @@ public final class LauncherInstrumentation { () -> mDevice.swipe(startX, startY, endX, endY, steps)); } - void swipeViaMovePointer( + void swipeWithModelTime( int startX, int startY, int endX, int endY, int expectedState, int steps) { - changeStateViaGesture(startX, startY, endX, endY, expectedState, () -> { - final long downTime = SystemClock.uptimeMillis(); - final Point start = new Point(startX, startY); - final Point end = new Point(endX, endY); - sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start); - final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, - end); - sendPointer( - downTime, endTime, MotionEvent.ACTION_UP, end); - }); + changeStateViaGesture(startX, startY, endX, endY, expectedState, + () -> swipeWithModelTime(startX, startY, endX, endY, steps)); + } + + void scrollWithModelTime( + UiObject2 container, Direction direction, float percent, Rect margins, int steps) { + final Rect rect = container.getVisibleBounds(); + if (margins != null) { + rect.left += margins.left; + rect.top += margins.top; + rect.right -= margins.right; + rect.bottom -= margins.bottom; + } + + final int startX; + final int startY; + final int endX; + final int endY; + + switch (direction) { + case UP: { + startX = endX = rect.centerX(); + final int vertCenter = rect.centerY(); + final float halfGestureHeight = rect.height() * percent / 2.0f; + startY = (int) (vertCenter - halfGestureHeight); + endY = (int) (vertCenter + halfGestureHeight); + } + break; + case DOWN: { + startX = endX = rect.centerX(); + final int vertCenter = rect.centerY(); + final float halfGestureHeight = rect.height() * percent / 2.0f; + startY = (int) (vertCenter + halfGestureHeight); + endY = (int) (vertCenter - halfGestureHeight); + } + break; + default: + fail("Unsupported direction"); + return; + } + + executeAndWaitForEvent( + () -> swipeWithModelTime(startX, startY, endX, endY, steps), + event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()), + "Didn't receive a scroll end message: " + startX + ", " + startY + + ", " + endX + ", " + endY); + } + + // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a + // fixed interval each time. + private void swipeWithModelTime(int startX, int startY, int endX, int endY, int steps) { + final long downTime = SystemClock.uptimeMillis(); + final Point start = new Point(startX, startY); + final Point end = new Point(endX, endY); + sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start); + final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end); + sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end); } private void changeStateViaGesture(int startX, int startY, int endX, int endY, @@ -673,10 +722,7 @@ public final class LauncherInstrumentation { } static void sleep(int duration) { - try { - Thread.sleep(duration); - } catch (InterruptedException e) { - } + SystemClock.sleep(duration); } int getEdgeSensitivityWidth() {