TextViewActivityTest.java revision 4bc3c9c100901ea10c755d78c4dd496e021c9379
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;
18
19import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
20import static android.support.test.espresso.action.ViewActions.longClick;
21import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
22import static android.widget.espresso.DragHandleUtils.onHandleView;
23import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem;
24import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
25import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
26import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
27import static android.widget.espresso.TextViewActions.dragHandle;
28import static android.widget.espresso.TextViewActions.Handle;
29import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
30import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
31import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
32import static android.widget.espresso.TextViewAssertions.hasSelection;
33import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
34import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
35import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
36import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
37import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
38import static android.support.test.espresso.Espresso.onView;
39import static android.support.test.espresso.action.ViewActions.click;
40import static android.support.test.espresso.action.ViewActions.pressKey;
41import static android.support.test.espresso.action.ViewActions.replaceText;
42import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
43import static android.support.test.espresso.assertion.ViewAssertions.matches;
44import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
45import static android.support.test.espresso.matcher.ViewMatchers.withId;
46import static android.support.test.espresso.matcher.ViewMatchers.withText;
47
48import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
49import com.android.frameworks.coretests.R;
50
51import android.support.test.espresso.action.EspressoKey;
52import android.test.ActivityInstrumentationTestCase2;
53import android.test.suitebuilder.annotation.SmallTest;
54import android.text.Selection;
55import android.text.Spannable;
56import android.text.InputType;
57import android.view.KeyEvent;
58
59import static org.hamcrest.Matchers.anyOf;
60import static org.hamcrest.Matchers.is;
61
62/**
63 * Tests the TextView widget from an Activity
64 */
65public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
66
67    public TextViewActivityTest() {
68        super(TextViewActivity.class);
69    }
70
71    @Override
72    public void setUp() {
73        getActivity();
74    }
75
76    @SmallTest
77    public void testTypedTextIsOnScreen() throws Exception {
78        final String helloWorld = "Hello world!";
79        onView(withId(R.id.textview)).perform(click());
80        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
81
82        onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
83    }
84
85    @SmallTest
86    public void testPositionCursorAtTextAtIndex() throws Exception {
87        final String helloWorld = "Hello world!";
88        onView(withId(R.id.textview)).perform(click());
89        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
90        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
91
92        // Delete text at specified index and see if we got the right one.
93        onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_FORWARD_DEL));
94        onView(withId(R.id.textview)).check(matches(withText("Hello orld!")));
95    }
96
97    @SmallTest
98    public void testPositionCursorAtTextAtIndex_arabic() throws Exception {
99        // Arabic text. The expected cursorable boundary is
100        // | \u0623 \u064F | \u067A | \u0633 \u0652 |
101        final String text = "\u0623\u064F\u067A\u0633\u0652";
102        onView(withId(R.id.textview)).perform(click());
103        onView(withId(R.id.textview)).perform(replaceText(text));
104
105        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
106        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
107        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(1));
108        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(anyOf(is(0), is(2))));
109        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(2));
110        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
111        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(3));
112        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(3));
113        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(4));
114        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(anyOf(is(3), is(5))));
115        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(5));
116        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(5));
117    }
118
119    @SmallTest
120    public void testPositionCursorAtTextAtIndex_devanagari() throws Exception {
121        // Devanagari text. The expected cursorable boundary is | \u0915 \u093E |
122        final String text = "\u0915\u093E";
123        onView(withId(R.id.textview)).perform(click());
124        onView(withId(R.id.textview)).perform(replaceText(text));
125
126        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
127        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
128        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(1));
129        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(anyOf(is(0), is(2))));
130        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(2));
131        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
132    }
133
134    @SmallTest
135    public void testLongPressToSelect() throws Exception {
136        final String helloWorld = "Hello Kirk!";
137        onView(withId(R.id.textview)).perform(click());
138        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
139        onView(withId(R.id.textview)).perform(
140                longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
141
142        onView(withId(R.id.textview)).check(hasSelection("Kirk"));
143    }
144
145    @SmallTest
146    public void testLongPressEmptySpace() throws Exception {
147        final String helloWorld = "Hello big round sun!";
148        onView(withId(R.id.textview)).perform(click());
149        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
150        // Move cursor somewhere else
151        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
152        // Long-press at end of line.
153        onView(withId(R.id.textview)).perform(longPressAtRelativeCoordinates(
154                RelativeCoordinatesProvider.HorizontalReference.RIGHT, -5,
155                RelativeCoordinatesProvider.VerticalReference.CENTER, 0));
156
157        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length()));
158    }
159
160    @SmallTest
161    public void testLongPressAndDragToSelect() throws Exception {
162        final String helloWorld = "Hello little handsome boy!";
163        onView(withId(R.id.textview)).perform(click());
164        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
165        onView(withId(R.id.textview)).perform(
166                longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
167
168        onView(withId(R.id.textview)).check(hasSelection("little handsome"));
169    }
170
171    @SmallTest
172    public void testDragAndDrop() throws Exception {
173        final String text = "abc def ghi.";
174        onView(withId(R.id.textview)).perform(click());
175        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
176        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
177
178        onView(withId(R.id.textview)).perform(
179                longPressAndDragOnText(text.indexOf("e"), text.length()));
180
181        onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
182        onView(withId(R.id.textview)).check(hasSelection(""));
183        assertNoSelectionHandles();
184        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
185
186        // Test undo returns to the original state.
187        onView(withId(R.id.textview)).perform(pressKey(
188                (new EspressoKey.Builder()).withCtrlPressed(true).withKeyCode(KeyEvent.KEYCODE_Z)
189                        .build()));
190        onView(withId(R.id.textview)).check(matches(withText(text)));
191    }
192
193    @SmallTest
194    public void testDoubleTapToSelect() throws Exception {
195        final String helloWorld = "Hello SuetYi!";
196        onView(withId(R.id.textview)).perform(click());
197        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
198        onView(withId(R.id.textview)).perform(
199                doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
200
201        onView(withId(R.id.textview)).check(hasSelection("SuetYi"));
202    }
203
204    @SmallTest
205    public void testDoubleTapAndDragToSelect() throws Exception {
206        final String helloWorld = "Hello young beautiful girl!";
207        onView(withId(R.id.textview)).perform(click());
208        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
209        onView(withId(R.id.textview)).perform(
210                doubleTapAndDragOnText(helloWorld.indexOf("young"), helloWorld.indexOf(" girl!")));
211
212        onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
213    }
214
215    @SmallTest
216    public void testSelectBackwordsByTouch() throws Exception {
217        final String helloWorld = "Hello king of the Jungle!";
218        onView(withId(R.id.textview)).perform(click());
219        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
220        onView(withId(R.id.textview)).perform(
221                doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
222
223        onView(withId(R.id.textview)).check(hasSelection("king of the"));
224    }
225
226    @SmallTest
227    public void testToolbarAppearsAfterSelection() throws Exception {
228        final String text = "Toolbar appears after selection.";
229        onView(withId(R.id.textview)).perform(click());
230        assertFloatingToolbarIsNotDisplayed();
231        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
232        onView(withId(R.id.textview)).perform(
233                longPressOnTextAtIndex(text.indexOf("appears")));
234
235        sleepForFloatingToolbarPopup();
236        assertFloatingToolbarIsDisplayed();
237
238        final String text2 = "Toolbar disappears after typing text.";
239        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text2));
240        assertFloatingToolbarIsNotDisplayed();
241    }
242
243    @SmallTest
244    public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint()
245            throws Exception {
246        // after the hint layout change, the floating toolbar was not visible in the case below
247        // this test tests that the floating toolbar is displayed on the screen and is visible to
248        // user.
249        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
250        textView.post(new Runnable() {
251            @Override
252            public void run() {
253                textView.setTextDirection(TextView.TEXT_DIRECTION_FIRST_STRONG_LTR);
254                textView.setInputType(InputType.TYPE_CLASS_TEXT);
255                textView.setSingleLine(true);
256                textView.setHint("الروبوت");
257            }
258        });
259        getInstrumentation().waitForIdleSync();
260
261        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test"));
262        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
263        onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click());
264        onView(withId(R.id.textview)).perform(longClick());
265        sleepForFloatingToolbarPopup();
266
267        assertFloatingToolbarIsDisplayed();
268    }
269
270    @SmallTest
271    public void testToolbarAndInsertionHandle() throws Exception {
272        final String text = "text";
273        onView(withId(R.id.textview)).perform(click());
274        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
275        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
276        assertFloatingToolbarIsNotDisplayed();
277
278        onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
279        sleepForFloatingToolbarPopup();
280        assertFloatingToolbarIsDisplayed();
281
282        assertFloatingToolbarContainsItem(
283                getActivity().getString(com.android.internal.R.string.selectAll));
284        assertFloatingToolbarDoesNotContainItem(
285                getActivity().getString(com.android.internal.R.string.copy));
286        assertFloatingToolbarDoesNotContainItem(
287                getActivity().getString(com.android.internal.R.string.cut));
288    }
289
290    @SmallTest
291    public void testToolbarAndSelectionHandle() throws Exception {
292        final String text = "abcd efg hijk";
293        onView(withId(R.id.textview)).perform(click());
294        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
295
296        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
297        sleepForFloatingToolbarPopup();
298        assertFloatingToolbarIsDisplayed();
299
300        assertFloatingToolbarContainsItem(
301                getActivity().getString(com.android.internal.R.string.selectAll));
302        assertFloatingToolbarContainsItem(
303                getActivity().getString(com.android.internal.R.string.copy));
304        assertFloatingToolbarContainsItem(
305                getActivity().getString(com.android.internal.R.string.cut));
306
307        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
308        onHandleView(com.android.internal.R.id.selection_start_handle)
309                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
310        sleepForFloatingToolbarPopup();
311        assertFloatingToolbarIsDisplayed();
312
313        onHandleView(com.android.internal.R.id.selection_end_handle)
314                .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
315        sleepForFloatingToolbarPopup();
316        assertFloatingToolbarIsDisplayed();
317
318        assertFloatingToolbarDoesNotContainItem(
319                getActivity().getString(com.android.internal.R.string.selectAll));
320        assertFloatingToolbarContainsItem(
321                getActivity().getString(com.android.internal.R.string.copy));
322        assertFloatingToolbarContainsItem(
323                getActivity().getString(com.android.internal.R.string.cut));
324    }
325
326    @SmallTest
327    public void testInsertionHandle() throws Exception {
328        final String text = "abcd efg hijk ";
329        onView(withId(R.id.textview)).perform(click());
330        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
331
332        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
333        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
334
335        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
336
337        onHandleView(com.android.internal.R.id.insertion_handle)
338                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
339        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
340
341        onHandleView(com.android.internal.R.id.insertion_handle)
342                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
343        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
344    }
345
346    @SmallTest
347    public void testInsertionHandle_multiLine() throws Exception {
348        final String text = "abcd\n" + "efg\n" + "hijk\n";
349        onView(withId(R.id.textview)).perform(click());
350        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
351
352        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
353        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
354
355        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
356
357        onHandleView(com.android.internal.R.id.insertion_handle)
358                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
359        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
360
361        onHandleView(com.android.internal.R.id.insertion_handle)
362                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
363        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
364    }
365
366    @SmallTest
367    public void testSelectionHandles() throws Exception {
368        final String text = "abcd efg hijk lmn";
369        onView(withId(R.id.textview)).perform(click());
370        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
371
372        assertNoSelectionHandles();
373
374        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
375
376        onHandleView(com.android.internal.R.id.selection_start_handle)
377                .check(matches(isDisplayed()));
378        onHandleView(com.android.internal.R.id.selection_end_handle)
379                .check(matches(isDisplayed()));
380
381        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
382        onHandleView(com.android.internal.R.id.selection_start_handle)
383                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
384        onView(withId(R.id.textview)).check(hasSelection("abcd efg"));
385
386        onHandleView(com.android.internal.R.id.selection_end_handle)
387                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('k') + 1));
388        onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk"));
389    }
390
391    @SmallTest
392    public void testSelectionHandles_multiLine() throws Exception {
393        final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
394        onView(withId(R.id.textview)).perform(click());
395        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
396        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
397
398        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
399        onHandleView(com.android.internal.R.id.selection_start_handle)
400                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('e')));
401        onView(withId(R.id.textview)).check(hasSelection("efg\nhijk"));
402
403        onHandleView(com.android.internal.R.id.selection_start_handle)
404                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
405        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk"));
406
407        onHandleView(com.android.internal.R.id.selection_end_handle)
408                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('n') + 1));
409        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn"));
410
411        onHandleView(com.android.internal.R.id.selection_end_handle)
412                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('r') + 1));
413        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr"));
414    }
415
416    @SmallTest
417    public void testSelectionHandles_multiLine_rtl() throws Exception {
418        // Arabic text.
419        final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
420                + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
421                + "\u0639\u063A\u063B";
422        onView(withId(R.id.textview)).perform(click());
423        onView(withId(R.id.textview)).perform(replaceText(text));
424        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
425        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('\u0634')));
426
427        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
428        onHandleView(com.android.internal.R.id.selection_start_handle)
429                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062E')));
430        onView(withId(R.id.textview)).check(hasSelection(
431                text.substring(text.indexOf('\u062D'), text.indexOf('\u0635') + 1)));
432
433        onHandleView(com.android.internal.R.id.selection_start_handle)
434                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062A')));
435        onView(withId(R.id.textview)).check(hasSelection(
436                text.substring(text.indexOf('\u062A'), text.indexOf('\u0635') + 1)));
437
438        onHandleView(com.android.internal.R.id.selection_end_handle)
439                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u0638')));
440        onView(withId(R.id.textview)).check(hasSelection(
441                text.substring(text.indexOf('\u062A'), text.indexOf('\u0638') + 1)));
442
443        onHandleView(com.android.internal.R.id.selection_end_handle)
444                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u063B')));
445        onView(withId(R.id.textview)).check(hasSelection(text));
446    }
447
448
449    @SmallTest
450    public void testSelectionHandles_doesNotPassAnotherHandle() throws Exception {
451        final String text = "abcd efg hijk lmn";
452        onView(withId(R.id.textview)).perform(click());
453        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
454        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
455
456        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
457        onHandleView(com.android.internal.R.id.selection_start_handle)
458                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('l')));
459        onView(withId(R.id.textview)).check(hasSelection("g"));
460
461        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
462        onHandleView(com.android.internal.R.id.selection_end_handle)
463                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a')));
464        onView(withId(R.id.textview)).check(hasSelection("e"));
465    }
466
467    @SmallTest
468    public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() throws Exception {
469        final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
470        onView(withId(R.id.textview)).perform(click());
471        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
472        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
473
474        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
475        onHandleView(com.android.internal.R.id.selection_start_handle)
476                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('r') + 1));
477        onView(withId(R.id.textview)).check(hasSelection("k"));
478
479        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
480        onHandleView(com.android.internal.R.id.selection_end_handle)
481                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a')));
482        onView(withId(R.id.textview)).check(hasSelection("h"));
483    }
484
485    @SmallTest
486    public void testSelectionHandles_snapToWordBoundary() throws Exception {
487        final String text = "abcd efg hijk lmn opqr";
488        onView(withId(R.id.textview)).perform(click());
489        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
490        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
491
492        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
493
494        onHandleView(com.android.internal.R.id.selection_start_handle)
495                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f')));
496        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
497
498        onHandleView(com.android.internal.R.id.selection_start_handle)
499                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('d') + 1));
500        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
501
502
503        onHandleView(com.android.internal.R.id.selection_start_handle)
504                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c')));
505        onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk"));
506
507        onHandleView(com.android.internal.R.id.selection_start_handle)
508                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('d')));
509        onView(withId(R.id.textview)).check(hasSelection("d efg hijk"));
510
511        onHandleView(com.android.internal.R.id.selection_start_handle)
512                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('b')));
513        onView(withId(R.id.textview)).check(hasSelection("bcd efg hijk"));
514
515        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
516
517        onHandleView(com.android.internal.R.id.selection_end_handle)
518                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('n')));
519        onView(withId(R.id.textview)).check(hasSelection("hijk lmn"));
520
521        onHandleView(com.android.internal.R.id.selection_end_handle)
522                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('o')));
523        onView(withId(R.id.textview)).check(hasSelection("hijk lmn"));
524
525        onHandleView(com.android.internal.R.id.selection_end_handle)
526                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('q')));
527        onView(withId(R.id.textview)).check(hasSelection("hijk lmn opqr"));
528
529        onHandleView(com.android.internal.R.id.selection_end_handle)
530                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('p')));
531        onView(withId(R.id.textview)).check(hasSelection("hijk lmn o"));
532
533        onHandleView(com.android.internal.R.id.selection_end_handle)
534                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('r')));
535        onView(withId(R.id.textview)).check(hasSelection("hijk lmn opq"));
536    }
537
538    @SmallTest
539    public void testSelectionHandles_snapToWordBoundary_multiLine() throws Exception {
540        final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu";
541        onView(withId(R.id.textview)).perform(click());
542        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
543        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('m')));
544
545        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
546
547        onHandleView(com.android.internal.R.id.selection_start_handle)
548                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c')));
549        onView(withId(R.id.textview)).check(hasSelection("abcd efg\nhijk lmn"));
550
551        onHandleView(com.android.internal.R.id.selection_start_handle)
552                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('g')));
553        onView(withId(R.id.textview)).check(hasSelection("g\nhijk lmn"));
554
555        onHandleView(com.android.internal.R.id.selection_start_handle)
556                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('m')));
557        onView(withId(R.id.textview)).check(hasSelection("lmn"));
558
559        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
560
561        onHandleView(com.android.internal.R.id.selection_end_handle)
562                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('u')));
563        onView(withId(R.id.textview)).check(hasSelection("hijk lmn\nopqr stu"));
564
565        onHandleView(com.android.internal.R.id.selection_end_handle)
566                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('p')));
567        onView(withId(R.id.textview)).check(hasSelection("hijk lmn\no"));
568
569        onHandleView(com.android.internal.R.id.selection_end_handle)
570                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
571        onView(withId(R.id.textview)).check(hasSelection("hijk"));
572    }
573
574    @SmallTest
575    public void testSetSelectionAndActionMode() throws Exception {
576        final String text = "abc def";
577        onView(withId(R.id.textview)).perform(click());
578        onView(withId(R.id.textview)).perform(replaceText(text));
579
580        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
581        assertFloatingToolbarIsNotDisplayed();
582        textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
583        getInstrumentation().waitForIdleSync();
584        sleepForFloatingToolbarPopup();
585        // Don't automatically start action mode.
586        assertFloatingToolbarIsNotDisplayed();
587        // Make sure that "Select All" is included in the selection action mode when the entire text
588        // is not selected.
589        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('e')));
590        sleepForFloatingToolbarPopup();
591        assertFloatingToolbarIsDisplayed();
592        // Changing the selection range by API should not interrupt the selection action mode.
593        textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
594        getInstrumentation().waitForIdleSync();
595        sleepForFloatingToolbarPopup();
596        assertFloatingToolbarIsDisplayed();
597        assertFloatingToolbarContainsItem(
598                getActivity().getString(com.android.internal.R.string.selectAll));
599        // Make sure that "Select All" is no longer included when the entire text is selected by
600        // API.
601        textView.post(
602                () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
603        getInstrumentation().waitForIdleSync();
604        sleepForFloatingToolbarPopup();
605        assertFloatingToolbarIsDisplayed();
606        assertFloatingToolbarDoesNotContainItem(
607                getActivity().getString(com.android.internal.R.string.selectAll));
608        // Make sure that shrinking the selection range to cursor (an empty range) by API
609        // terminates selection action mode and does not trigger the insertion action mode.
610        textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0));
611        getInstrumentation().waitForIdleSync();
612        sleepForFloatingToolbarPopup();
613        assertFloatingToolbarIsNotDisplayed();
614        // Make sure that user click can trigger the insertion action mode.
615        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
616        onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
617        sleepForFloatingToolbarPopup();
618        assertFloatingToolbarIsDisplayed();
619        // Make sure that an existing insertion action mode keeps alive after the insertion point is
620        // moved by API.
621        textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0));
622        getInstrumentation().waitForIdleSync();
623        sleepForFloatingToolbarPopup();
624        assertFloatingToolbarIsDisplayed();
625        assertFloatingToolbarDoesNotContainItem(
626                getActivity().getString(com.android.internal.R.string.copy));
627        // Make sure that selection action mode is started after selection is created by API when
628        // insertion action mode is active.
629        textView.post(
630                () -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
631        getInstrumentation().waitForIdleSync();
632        sleepForFloatingToolbarPopup();
633        assertFloatingToolbarIsDisplayed();
634        assertFloatingToolbarContainsItem(
635                getActivity().getString(com.android.internal.R.string.copy));
636    }
637}
638