TextViewActions.java revision da79ee683d74f2ad7d29147a30f3da17dd0ea6e2
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package android.widget.espresso; 18 19import static android.support.test.espresso.action.ViewActions.actionWithAssertions; 20import android.graphics.Rect; 21import android.support.test.espresso.PerformException; 22import android.support.test.espresso.ViewAction; 23import android.support.test.espresso.action.CoordinatesProvider; 24import android.support.test.espresso.action.GeneralClickAction; 25import android.support.test.espresso.action.Press; 26import android.support.test.espresso.action.Tap; 27import android.support.test.espresso.util.HumanReadables; 28import android.text.Layout; 29import android.view.View; 30import android.widget.Editor; 31import android.widget.TextView; 32 33/** 34 * A collection of actions on a {@link android.widget.TextView}. 35 */ 36public final class TextViewActions { 37 38 private TextViewActions() {} 39 40 /** 41 * Returns an action that clicks on text at an index on the TextView.<br> 42 * <br> 43 * View constraints: 44 * <ul> 45 * <li>must be a TextView displayed on screen 46 * <ul> 47 * 48 * @param index The index of the TextView's text to click on. 49 */ 50 public static ViewAction clickOnTextAtIndex(int index) { 51 return actionWithAssertions( 52 new GeneralClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER)); 53 } 54 55 /** 56 * Returns an action that double-clicks on text at an index on the TextView.<br> 57 * <br> 58 * View constraints: 59 * <ul> 60 * <li>must be a TextView displayed on screen 61 * <ul> 62 * 63 * @param index The index of the TextView's text to double-click on. 64 */ 65 public static ViewAction doubleClickOnTextAtIndex(int index) { 66 return actionWithAssertions( 67 new GeneralClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER)); 68 } 69 70 /** 71 * Returns an action that long presses on text at an index on the TextView.<br> 72 * <br> 73 * View constraints: 74 * <ul> 75 * <li>must be a TextView displayed on screen 76 * <ul> 77 * 78 * @param index The index of the TextView's text to long press on. 79 */ 80 public static ViewAction longPressOnTextAtIndex(int index) { 81 return actionWithAssertions( 82 new GeneralClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER)); 83 } 84 85 /** 86 * Returns an action that long presses then drags on text from startIndex to endIndex on the 87 * TextView.<br> 88 * <br> 89 * View constraints: 90 * <ul> 91 * <li>must be a TextView displayed on screen 92 * <ul> 93 * 94 * @param startIndex The index of the TextView's text to start a drag from 95 * @param endIndex The index of the TextView's text to end the drag at 96 */ 97 public static ViewAction longPressAndDragOnText(int startIndex, int endIndex) { 98 return actionWithAssertions( 99 new DragAction( 100 DragAction.Drag.LONG_PRESS, 101 new TextCoordinates(startIndex), 102 new TextCoordinates(endIndex), 103 Press.FINGER, 104 TextView.class)); 105 } 106 107 /** 108 * Returns an action that double taps then drags on text from startIndex to endIndex on the 109 * TextView.<br> 110 * <br> 111 * View constraints: 112 * <ul> 113 * <li>must be a TextView displayed on screen 114 * <ul> 115 * 116 * @param startIndex The index of the TextView's text to start a drag from 117 * @param endIndex The index of the TextView's text to end the drag at 118 */ 119 public static ViewAction doubleTapAndDragOnText(int startIndex, int endIndex) { 120 return actionWithAssertions( 121 new DragAction( 122 DragAction.Drag.DOUBLE_TAP, 123 new TextCoordinates(startIndex), 124 new TextCoordinates(endIndex), 125 Press.FINGER, 126 TextView.class)); 127 } 128 129 /** 130 * Returns an action that click then drags by mouse on text from startIndex to endIndex on the 131 * TextView.<br> 132 * <br> 133 * View constraints: 134 * <ul> 135 * <li>must be a TextView displayed on screen 136 * <ul> 137 * 138 * @param startIndex The index of the TextView's text to start a drag from 139 * @param endIndex The index of the TextView's text to end the drag at 140 */ 141 public static ViewAction mouseDragOnText(int startIndex, int endIndex) { 142 return actionWithAssertions( 143 new DragAction( 144 DragAction.Drag.MOUSE_DOWN, 145 new TextCoordinates(startIndex), 146 new TextCoordinates(endIndex), 147 Press.PINPOINT, 148 TextView.class)); 149 } 150 151 public enum Handle { 152 SELECTION_START, 153 SELECTION_END, 154 INSERTION 155 }; 156 157 /** 158 * Returns an action that tap then drags on the handle from the current position to endIndex on 159 * the TextView.<br> 160 * <br> 161 * View constraints: 162 * <ul> 163 * <li>must be a TextView's drag-handle displayed on screen 164 * <ul> 165 * 166 * @param textView TextView the handle is on 167 * @param handleType Type of the handle 168 * @param endIndex The index of the TextView's text to end the drag at 169 */ 170 public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex) { 171 final int currentOffset = handleType == Handle.SELECTION_START ? 172 textView.getSelectionStart() : textView.getSelectionEnd(); 173 return actionWithAssertions( 174 new DragAction( 175 DragAction.Drag.TAP, 176 new HandleCoordinates(textView, handleType, currentOffset), 177 new HandleCoordinates(textView, handleType, endIndex), 178 Press.FINGER, 179 Editor.HandleView.class)); 180 } 181 182 /** 183 * A provider of the x, y coordinates of the handle that points the specified text index in a 184 * text view. 185 */ 186 private static final class HandleCoordinates implements CoordinatesProvider { 187 private final TextView mTextView; 188 private final Handle mHandleType; 189 private final int mIndex; 190 private final String mActionDescription; 191 192 public HandleCoordinates(TextView textView, Handle handleType, int index) { 193 mTextView = textView; 194 mHandleType = handleType; 195 mIndex = index; 196 mActionDescription = "Could not locate " + handleType.toString() 197 + " handle that points text index: " + index; 198 } 199 200 @Override 201 public float[] calculateCoordinates(View view) { 202 try { 203 return locateHandlePointsTextIndex(view); 204 } catch (StringIndexOutOfBoundsException e) { 205 throw new PerformException.Builder() 206 .withActionDescription(mActionDescription) 207 .withViewDescription(HumanReadables.describe(view)) 208 .withCause(e) 209 .build(); 210 } 211 } 212 213 private float[] locateHandlePointsTextIndex(View view) { 214 final int currentOffset = mHandleType == Handle.SELECTION_START ? 215 mTextView.getSelectionStart() : mTextView.getSelectionEnd(); 216 final float[] currentCoordinates = 217 (new TextCoordinates(currentOffset)).calculateCoordinates(mTextView); 218 final float[] targetCoordinates = 219 (new TextCoordinates(mIndex)).calculateCoordinates(mTextView); 220 final Rect bounds = new Rect(); 221 view.getBoundsOnScreen(bounds); 222 final float diffX = bounds.centerX() - currentCoordinates[0]; 223 final float diffY = bounds.centerY() - currentCoordinates[1]; 224 return new float[] {targetCoordinates[0] + diffX, targetCoordinates[1] + diffY}; 225 } 226 } 227 228 /** 229 * A provider of the x, y coordinates of the text at the specified index in a text view. 230 */ 231 private static final class TextCoordinates implements CoordinatesProvider { 232 233 private final int mIndex; 234 private final String mActionDescription; 235 236 public TextCoordinates(int index) { 237 mIndex = index; 238 mActionDescription = "Could not locate text at index: " + mIndex; 239 } 240 241 @Override 242 public float[] calculateCoordinates(View view) { 243 try { 244 return locateTextAtIndex((TextView) view, mIndex); 245 } catch (ClassCastException e) { 246 throw new PerformException.Builder() 247 .withActionDescription(mActionDescription) 248 .withViewDescription(HumanReadables.describe(view)) 249 .withCause(e) 250 .build(); 251 } catch (StringIndexOutOfBoundsException e) { 252 throw new PerformException.Builder() 253 .withActionDescription(mActionDescription) 254 .withViewDescription(HumanReadables.describe(view)) 255 .withCause(e) 256 .build(); 257 } 258 } 259 260 /** 261 * @throws StringIndexOutOfBoundsException 262 */ 263 private float[] locateTextAtIndex(TextView textView, int index) { 264 if (index < 0 || index > textView.getText().length()) { 265 throw new StringIndexOutOfBoundsException(index); 266 } 267 final int[] xy = new int[2]; 268 textView.getLocationOnScreen(xy); 269 final Layout layout = textView.getLayout(); 270 final int line = layout.getLineForOffset(index); 271 final float x = textView.getTotalPaddingLeft() - textView.getScrollX() 272 + layout.getPrimaryHorizontal(index); 273 final float y = textView.getTotalPaddingTop() - textView.getScrollY() 274 + layout.getLineTop(line); 275 return new float[]{x + xy[0], y + xy[1]}; 276 } 277 } 278} 279