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