From da79ee683d74f2ad7d29147a30f3da17dd0ea6e2 Mon Sep 17 00:00:00 2001 From: Keisuke Kuroyanagi Date: Wed, 25 Nov 2015 16:15:15 +0900 Subject: [PATCH] Add selection handle dragging tests. - Set an id for each HandleView to access the hanlde. - Rename DragOnTextViewActions to DragAction to use it for dragging handles. - Introduce HandleCoordinates to provide proper coordinates for handles. More tests will be added in following CLs. Bug: 25730231 Change-Id: I9276bf2f983983ec9aae0ddcf674d3dcee566892 --- core/java/android/widget/Editor.java | 12 ++- core/res/res/values/ids.xml | 3 + core/res/res/values/symbols.xml | 3 + .../android/widget/TextViewActivityTest.java | 29 +++++ ...OnTextViewActions.java => DragAction.java} | 53 +++++++-- .../widget/espresso/TextViewActions.java | 101 ++++++++++++++++-- 6 files changed, 175 insertions(+), 26 deletions(-) rename core/tests/coretests/src/android/widget/espresso/{DragOnTextViewActions.java => DragAction.java} (88%) diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 2fabe33ddf39a..8cd4de39c3d9d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3494,7 +3494,8 @@ public class Editor { } } - private abstract class HandleView extends View implements TextViewPositionListener { + @VisibleForTesting + public abstract class HandleView extends View implements TextViewPositionListener { protected Drawable mDrawable; protected Drawable mDrawableLtr; protected Drawable mDrawableRtl; @@ -3525,8 +3526,9 @@ public class Editor { // a different line. protected int mPreviousLineTouched = UNSET_LINE; - public HandleView(Drawable drawableLtr, Drawable drawableRtl) { + private HandleView(Drawable drawableLtr, Drawable drawableRtl, final int id) { super(mTextView.getContext()); + setId(id); mContainer = new PopupWindow(mTextView.getContext(), null, com.android.internal.R.attr.textSelectHandleWindowStyle); mContainer.setSplitTouchEnabled(true); @@ -3888,7 +3890,7 @@ public class Editor { private Runnable mHider; public InsertionHandleView(Drawable drawable) { - super(drawable, drawable); + super(drawable, drawable, com.android.internal.R.id.insertion_handle); } @Override @@ -4073,7 +4075,7 @@ public class Editor { private final int[] mTextViewLocation = new int[2]; public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) { - super(drawableLtr, drawableRtl); + super(drawableLtr, drawableRtl, com.android.internal.R.id.selection_start_handle); ViewConfiguration viewConfiguration = ViewConfiguration.get( mTextView.getContext()); mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4; @@ -4313,7 +4315,7 @@ public class Editor { private final int[] mTextViewLocation = new int[2]; public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) { - super(drawableLtr, drawableRtl); + super(drawableLtr, drawableRtl, com.android.internal.R.id.selection_end_handle); ViewConfiguration viewConfiguration = ViewConfiguration.get( mTextView.getContext()); mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4; diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 695dafac40b60..a34876727945e 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -94,6 +94,9 @@ + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cda7faa1f09bb..80e5668880edd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -221,6 +221,9 @@ + + + diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index bb515701ef8e1..b03552f9e908f 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -19,6 +19,8 @@ package android.widget; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex; +import static android.widget.espresso.TextViewActions.dragHandle; +import static android.widget.espresso.TextViewActions.Handle; import static android.widget.espresso.TextViewActions.longPressAndDragOnText; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; @@ -29,11 +31,16 @@ import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.pressKey; import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView; import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.RootMatchers.withDecorView; +import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; +import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.allOf; import com.android.frameworks.coretests.R; +import android.support.test.espresso.ViewInteraction; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.KeyEvent; @@ -157,4 +164,26 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2 + * Drags on a View using touch events.
*
* View constraints: *
    - *
  • must be a TextView displayed on screen + *
  • must be displayed on screen *
      */ -public final class DragOnTextViewActions implements ViewAction { +public final class DragAction implements ViewAction { public interface Dragger extends Swiper { UiController wrapUiController(UiController uiController); } /** - * Executes different "drag on text" types to given positions. + * Executes different drag types to given positions. */ public enum Drag implements Dragger { @@ -82,7 +81,7 @@ public final class DragOnTextViewActions implements ViewAction { @Override public String toString() { - return "mouse down and drag to select"; + return "mouse down and drag"; } @Override @@ -91,6 +90,35 @@ public final class DragOnTextViewActions implements ViewAction { } }, + /** + * Starts a drag with a tap. + */ + TAP { + private DownMotionPerformer downMotion = new DownMotionPerformer() { + @Override + public MotionEvent perform( + UiController uiController, float[] coordinates, float[] precision) { + MotionEvent downEvent = MotionEvents.sendDown( + uiController, coordinates, precision) + .down; + return downEvent; + } + }; + + @Override + public Status sendSwipe( + UiController uiController, + float[] startCoordinates, float[] endCoordinates, float[] precision) { + return sendLinearDrag( + uiController, downMotion, startCoordinates, endCoordinates, precision); + } + + @Override + public String toString() { + return "tap and drag"; + } + }, + /** * Starts a drag with a long-press. */ @@ -121,7 +149,7 @@ public final class DragOnTextViewActions implements ViewAction { @Override public String toString() { - return "long press and drag to select"; + return "long press and drag"; } }, @@ -166,7 +194,7 @@ public final class DragOnTextViewActions implements ViewAction { @Override public String toString() { - return "double-tap and drag to select"; + return "double-tap and drag"; } }; @@ -258,22 +286,25 @@ public final class DragOnTextViewActions implements ViewAction { private final CoordinatesProvider mStartCoordinatesProvider; private final CoordinatesProvider mEndCoordinatesProvider; private final PrecisionDescriber mPrecisionDescriber; + private final Class mViewClass; - public DragOnTextViewActions( + public DragAction( Dragger dragger, CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider, - PrecisionDescriber precisionDescriber) { + PrecisionDescriber precisionDescriber, + Class viewClass) { mDragger = checkNotNull(dragger); mStartCoordinatesProvider = checkNotNull(startCoordinatesProvider); mEndCoordinatesProvider = checkNotNull(endCoordinatesProvider); mPrecisionDescriber = checkNotNull(precisionDescriber); + mViewClass = viewClass; } @Override @SuppressWarnings("unchecked") public Matcher getConstraints() { - return allOf(isCompletelyDisplayed(), isAssignableFrom(TextView.class)); + return allOf(isCompletelyDisplayed(), isAssignableFrom(mViewClass)); } @Override diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java index 4f5a72b14b2a2..b84afff18d9b2 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java @@ -17,7 +17,7 @@ package android.widget.espresso; import static android.support.test.espresso.action.ViewActions.actionWithAssertions; - +import android.graphics.Rect; import android.support.test.espresso.PerformException; import android.support.test.espresso.ViewAction; import android.support.test.espresso.action.CoordinatesProvider; @@ -27,6 +27,7 @@ import android.support.test.espresso.action.Tap; import android.support.test.espresso.util.HumanReadables; import android.text.Layout; import android.view.View; +import android.widget.Editor; import android.widget.TextView; /** @@ -95,11 +96,12 @@ public final class TextViewActions { */ public static ViewAction longPressAndDragOnText(int startIndex, int endIndex) { return actionWithAssertions( - new DragOnTextViewActions( - DragOnTextViewActions.Drag.LONG_PRESS, + new DragAction( + DragAction.Drag.LONG_PRESS, new TextCoordinates(startIndex), new TextCoordinates(endIndex), - Press.FINGER)); + Press.FINGER, + TextView.class)); } /** @@ -116,11 +118,12 @@ public final class TextViewActions { */ public static ViewAction doubleTapAndDragOnText(int startIndex, int endIndex) { return actionWithAssertions( - new DragOnTextViewActions( - DragOnTextViewActions.Drag.DOUBLE_TAP, + new DragAction( + DragAction.Drag.DOUBLE_TAP, new TextCoordinates(startIndex), new TextCoordinates(endIndex), - Press.FINGER)); + Press.FINGER, + TextView.class)); } /** @@ -137,11 +140,89 @@ public final class TextViewActions { */ public static ViewAction mouseDragOnText(int startIndex, int endIndex) { return actionWithAssertions( - new DragOnTextViewActions( - DragOnTextViewActions.Drag.MOUSE_DOWN, + new DragAction( + DragAction.Drag.MOUSE_DOWN, new TextCoordinates(startIndex), new TextCoordinates(endIndex), - Press.PINPOINT)); + Press.PINPOINT, + TextView.class)); + } + + public enum Handle { + SELECTION_START, + SELECTION_END, + INSERTION + }; + + /** + * Returns an action that tap then drags on the handle from the current position to endIndex on + * the TextView.
      + *
      + * View constraints: + *
        + *
      • must be a TextView's drag-handle displayed on screen + *
          + * + * @param textView TextView the handle is on + * @param handleType Type of the handle + * @param endIndex The index of the TextView's text to end the drag at + */ + public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex) { + final int currentOffset = handleType == Handle.SELECTION_START ? + textView.getSelectionStart() : textView.getSelectionEnd(); + return actionWithAssertions( + new DragAction( + DragAction.Drag.TAP, + new HandleCoordinates(textView, handleType, currentOffset), + new HandleCoordinates(textView, handleType, endIndex), + Press.FINGER, + Editor.HandleView.class)); + } + + /** + * A provider of the x, y coordinates of the handle that points the specified text index in a + * text view. + */ + private static final class HandleCoordinates implements CoordinatesProvider { + private final TextView mTextView; + private final Handle mHandleType; + private final int mIndex; + private final String mActionDescription; + + public HandleCoordinates(TextView textView, Handle handleType, int index) { + mTextView = textView; + mHandleType = handleType; + mIndex = index; + mActionDescription = "Could not locate " + handleType.toString() + + " handle that points text index: " + index; + } + + @Override + public float[] calculateCoordinates(View view) { + try { + return locateHandlePointsTextIndex(view); + } catch (StringIndexOutOfBoundsException e) { + throw new PerformException.Builder() + .withActionDescription(mActionDescription) + .withViewDescription(HumanReadables.describe(view)) + .withCause(e) + .build(); + } + } + + private float[] locateHandlePointsTextIndex(View view) { + final int currentOffset = mHandleType == Handle.SELECTION_START ? + mTextView.getSelectionStart() : mTextView.getSelectionEnd(); + final float[] currentCoordinates = + (new TextCoordinates(currentOffset)).calculateCoordinates(mTextView); + final float[] targetCoordinates = + (new TextCoordinates(mIndex)).calculateCoordinates(mTextView); + final Rect bounds = new Rect(); + view.getBoundsOnScreen(bounds); + final float diffX = bounds.centerX() - currentCoordinates[0]; + final float diffY = bounds.centerY() - currentCoordinates[1]; + return new float[] {targetCoordinates[0] + diffX, targetCoordinates[1] + diffY}; + } } /**