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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -94,6 +94,9 @@
|
||||
<item type="id" name="redo" />
|
||||
<item type="id" name="replaceText" />
|
||||
<item type="id" name="shareText" />
|
||||
<item type="id" name="selection_start_handle" />
|
||||
<item type="id" name="selection_end_handle" />
|
||||
<item type="id" name="insertion_handle" />
|
||||
<item type="id" name="floating_toolbar_menu_item_image_button" />
|
||||
|
||||
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
|
||||
|
||||
@@ -221,6 +221,9 @@
|
||||
<java-symbol type="id" name="profile_badge_line2" />
|
||||
<java-symbol type="id" name="profile_badge_line3" />
|
||||
<java-symbol type="id" name="transitionPosition" />
|
||||
<java-symbol type="id" name="selection_start_handle" />
|
||||
<java-symbol type="id" name="selection_end_handle" />
|
||||
<java-symbol type="id" name="insertion_handle" />
|
||||
|
||||
<java-symbol type="attr" name="actionModeShareDrawable" />
|
||||
<java-symbol type="attr" name="alertDialogCenterButtons" />
|
||||
|
||||
@@ -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<TextV
|
||||
Thread.sleep(100);
|
||||
assertFloatingToolbarIsDisplayed(getActivity());
|
||||
}
|
||||
|
||||
private static ViewInteraction onHandleView(int id) {
|
||||
return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
|
||||
.inRoot(withDecorView(hasDescendant(withId(id))));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testSelectionHandles() throws Exception {
|
||||
final String text = "abcd efg hijk lmn";
|
||||
onView(withId(R.id.textview)).perform(click());
|
||||
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
|
||||
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
|
||||
|
||||
final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
|
||||
onHandleView(com.android.internal.R.id.selection_start_handle)
|
||||
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
|
||||
onView(withId(R.id.textview)).check(hasSelection("abcd efg"));
|
||||
|
||||
onHandleView(com.android.internal.R.id.selection_end_handle)
|
||||
.perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('k') + 1));
|
||||
onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,26 +34,25 @@ import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
|
||||
|
||||
/**
|
||||
* Drags on text in a TextView using touch events.<br>
|
||||
* Drags on a View using touch events.<br>
|
||||
* <br>
|
||||
* View constraints:
|
||||
* <ul>
|
||||
* <li>must be a TextView displayed on screen
|
||||
* <li>must be displayed on screen
|
||||
* <ul>
|
||||
*/
|
||||
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<? extends View> mViewClass;
|
||||
|
||||
public DragOnTextViewActions(
|
||||
public DragAction(
|
||||
Dragger dragger,
|
||||
CoordinatesProvider startCoordinatesProvider,
|
||||
CoordinatesProvider endCoordinatesProvider,
|
||||
PrecisionDescriber precisionDescriber) {
|
||||
PrecisionDescriber precisionDescriber,
|
||||
Class<? extends View> viewClass) {
|
||||
mDragger = checkNotNull(dragger);
|
||||
mStartCoordinatesProvider = checkNotNull(startCoordinatesProvider);
|
||||
mEndCoordinatesProvider = checkNotNull(endCoordinatesProvider);
|
||||
mPrecisionDescriber = checkNotNull(precisionDescriber);
|
||||
mViewClass = viewClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Matcher<View> getConstraints() {
|
||||
return allOf(isCompletelyDisplayed(), isAssignableFrom(TextView.class));
|
||||
return allOf(isCompletelyDisplayed(), isAssignableFrom(mViewClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -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.<br>
|
||||
* <br>
|
||||
* View constraints:
|
||||
* <ul>
|
||||
* <li>must be a TextView's drag-handle displayed on screen
|
||||
* <ul>
|
||||
*
|
||||
* @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};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user