From ff3fa34a7ab5feb0bbe11893022a832be73536d5 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Wed, 25 Jul 2018 16:29:06 -0700 Subject: [PATCH] Fixing scrolling up in App Apps. Done by scrolling only when scroll position is not zero. This way, the scroll gesture can't close All Apps. Bug: 110103162 Test: TaplTests suite Change-Id: Icfe47d2bcc0210ae221df169d6c35cd1be10ff86 --- src/com/android/launcher3/Utilities.java | 4 ++ .../allapps/AllAppsContainerView.java | 14 +++++ .../compat/AccessibilityManagerCompat.java | 61 ++++++++++++++++--- .../com/android/launcher3/tapl/AllApps.java | 23 ++++--- .../launcher3/tapl/AllAppsFromOverview.java | 5 -- .../tapl/LauncherInstrumentation.java | 22 +++++-- 6 files changed, 102 insertions(+), 27 deletions(-) diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 7c5bb1af8f..a8513185e5 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -602,4 +602,8 @@ public final class Utilities { msg.setAsynchronous(true); handler.sendMessage(msg); } + + public interface Consumer { + void accept(T var1); + } } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index fdf32af6d2..40cf0f3d07 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; +import android.os.Bundle; import android.os.Process; import android.support.animation.DynamicAnimation; import android.support.annotation.NonNull; @@ -48,6 +49,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -549,4 +551,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo && verticalFadingEdge); } } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (AccessibilityManagerCompat.processTestRequest( + mLauncher, "TAPL_GET_SCROLL", action, arguments, + response -> + response.putInt("scrollY", getActiveRecyclerView().getCurrentScrollY()))) { + return true; + } + + return super.performAccessibilityAction(action, arguments); + } } diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index 29fc2bc60d..32fb5332c0 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -18,9 +18,11 @@ package com.android.launcher3.compat; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; +import android.os.Bundle; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import com.android.launcher3.Utilities; @@ -49,17 +51,60 @@ public class AccessibilityManagerCompat { } public static void sendEventToTest(Context context, String eventTag) { - if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return; + final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); + if (accessibilityManager == null) return; + sendEventToTest(accessibilityManager, eventTag, null); + } + + private static void sendEventToTest( + AccessibilityManager accessibilityManager, String eventTag, Bundle data) { + final AccessibilityEvent e = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_ANNOUNCEMENT); + e.setClassName(eventTag); + e.setParcelableData(data); + accessibilityManager.sendAccessibilityEvent(e); + } + + /** + * Returns accessibility manager to be used for communication with UI Automation tests. + * The tests may exchange custom accessibility messages with the launcher; the accessibility + * manager is used in these communications. + * + * If the launcher runs not under a test, the return is null, and no attempt to process or send + * custom accessibility messages should be made. + */ + private static AccessibilityManager getAccessibilityManagerForTest(Context context) { + // If not running in a test harness, don't participate in test exchanges. + if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return null; + + // Additional safety check: when running under UI Automation, accessibility is enabled, + // but the list of accessibility services is empty. Return null if this is not the case. final AccessibilityManager accessibilityManager = getManager(context); - if (accessibilityManager.isEnabled() && + if (!accessibilityManager.isEnabled() || accessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_ALL_MASK).size() == 0) { - - final AccessibilityEvent e = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_ANNOUNCEMENT); - e.setClassName(eventTag); - accessibilityManager.sendAccessibilityEvent(e); + AccessibilityServiceInfo.FEEDBACK_ALL_MASK).size() > 0) { + return null; } + + return accessibilityManager; + } + + public static boolean processTestRequest(Context context, String eventTag, int action, + Bundle request, Utilities.Consumer responseFiller) { + final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); + if (accessibilityManager == null) return false; + + // The test sends a request via a ACTION_SET_TEXT. + if (action == AccessibilityNodeInfo.ACTION_SET_TEXT && + eventTag.equals(request.getCharSequence( + AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE))) { + final Bundle response = new Bundle(); + responseFiller.accept(response); + AccessibilityManagerCompat.sendEventToTest( + accessibilityManager, eventTag + "_RESPONSE", response); + return true; + } + return false; } } diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index d849e2d809..e270b46056 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -72,28 +72,33 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { return new AppIcon(mLauncher, appIcon); } - protected int getBottomMarginForSwipeUp() { - return 5; - } - private void scrollBackToBeginning() { final UiObject2 allAppsContainer = verifyActiveContainer(); + final UiObject2 searchBox = + mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps"); int attempts = 0; - allAppsContainer.setGestureMargins(5, 600, 5, getBottomMarginForSwipeUp()); + allAppsContainer.setGestureMargins(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); - while (allAppsContainer.scroll(Direction.UP, 0.5f)) { - mLauncher.waitForIdle(); - verifyActiveContainer(); + for (int scroll = getScroll(allAppsContainer); + scroll != 0; + scroll = getScroll(allAppsContainer)) { + assertTrue("Negative scroll position", scroll > 0); assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS, ++attempts <= MAX_SCROLL_ATTEMPTS); + + allAppsContainer.scroll(Direction.UP, 1); } - mLauncher.waitForIdle(); verifyActiveContainer(); } + private int getScroll(UiObject2 allAppsContainer) { + return mLauncher.getAnswerFromLauncher(allAppsContainer, "TAPL_GET_SCROLL"). + getInt("scrollY", -1); + } + private void ensureIconVisible(UiObject2 appIcon, UiObject2 allAppsContainer) { final int appHeight = appIcon.getVisibleBounds().height(); if (appHeight < MIN_INTERACT_SIZE) { diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java index bc0dfc6462..7ed2dc2f6d 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java @@ -30,11 +30,6 @@ public final class AllAppsFromOverview extends AllApps { verifyActiveContainer(); } - @Override - protected int getBottomMarginForSwipeUp() { - return 600; - } - /** * Swipes down to switch back to Overview whence we came from. * diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index fc32fed2cc..0be09f0d9d 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -23,6 +23,7 @@ import static org.junit.Assert.fail; import android.app.ActivityManager; import android.app.UiAutomation; import android.content.res.Resources; +import android.os.Bundle; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -32,8 +33,7 @@ import android.support.test.uiautomator.BySelector; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; - -import org.junit.Assert; +import android.view.accessibility.AccessibilityEvent; import java.lang.ref.WeakReference; import java.util.concurrent.TimeoutException; @@ -156,18 +156,30 @@ public final class LauncherInstrumentation { } } - private void executeAndWaitForEvent(Runnable command, + private Bundle executeAndWaitForEvent(Runnable command, UiAutomation.AccessibilityEventFilter eventFilter, String message) { try { - assertNotNull("executeAndWaitForEvent returned null (this can't happen)", + final AccessibilityEvent event = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeAndWaitForEvent( - command, eventFilter, WAIT_TIME_MS)); + command, eventFilter, WAIT_TIME_MS); + assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event); + return (Bundle) event.getParcelableData(); } catch (TimeoutException e) { fail(message); + return null; } } + Bundle getAnswerFromLauncher(UiObject2 view, String requestTag) { + // Send a fake set-text request to Launcher to initiate a response with requested data. + final String responseTag = requestTag + "_RESPONSE"; + return executeAndWaitForEvent( + () -> view.setText(requestTag), + event -> responseTag.equals(event.getClassName()), + "Launcher didn't respond to request: " + requestTag); + } + /** * Presses nav bar home button. *