TextViewActivityTest.java revision 29cb76849c94bdbd95439e372360a51720c6b067
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.support.test.espresso.Espresso.onView;
20import static android.support.test.espresso.action.ViewActions.click;
21import static android.support.test.espresso.action.ViewActions.longClick;
22import static android.support.test.espresso.action.ViewActions.pressKey;
23import static android.support.test.espresso.action.ViewActions.replaceText;
24import static android.support.test.espresso.assertion.ViewAssertions.matches;
25import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
26import static android.support.test.espresso.matcher.ViewMatchers.withId;
27import static android.support.test.espresso.matcher.ViewMatchers.withText;
28import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
29import static android.widget.espresso.DragHandleUtils.onHandleView;
30import static android.widget.espresso.FloatingToolbarEspressoUtils
31        .assertFloatingToolbarContainsItem;
32import static android.widget.espresso.FloatingToolbarEspressoUtils
33        .assertFloatingToolbarDoesNotContainItem;
34import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
35import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
36import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
37import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
38import static android.widget.espresso.TextViewActions.Handle;
39import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
40import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
41import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
42import static android.widget.espresso.TextViewActions.dragHandle;
43import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
44import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
45import static android.widget.espresso.TextViewAssertions.doesNotHaveStyledText;
46import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
47import static android.widget.espresso.TextViewAssertions.hasSelection;
48
49import static junit.framework.Assert.assertFalse;
50import static junit.framework.Assert.assertTrue;
51
52import static org.hamcrest.Matchers.anyOf;
53import static org.hamcrest.Matchers.is;
54import static org.mockito.Matchers.any;
55import static org.mockito.Mockito.mock;
56import static org.mockito.Mockito.never;
57import static org.mockito.Mockito.verify;
58import static org.mockito.Mockito.when;
59
60import android.app.Activity;
61import android.app.Instrumentation;
62import android.content.ClipData;
63import android.content.ClipboardManager;
64import android.support.test.InstrumentationRegistry;
65import android.support.test.espresso.action.EspressoKey;
66import android.support.test.filters.MediumTest;
67import android.support.test.rule.ActivityTestRule;
68import android.support.test.runner.AndroidJUnit4;
69import android.support.test.uiautomator.UiDevice;
70import android.test.suitebuilder.annotation.Suppress;
71import android.text.InputType;
72import android.text.Selection;
73import android.text.Spannable;
74import android.text.SpannableString;
75import android.text.method.LinkMovementMethod;
76import android.view.ActionMode;
77import android.view.KeyEvent;
78import android.view.Menu;
79import android.view.MenuItem;
80import android.view.textclassifier.TextClassificationManager;
81import android.view.textclassifier.TextClassifier;
82import android.view.textclassifier.TextLinks;
83import android.view.textclassifier.TextLinksParams;
84import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
85
86import com.android.frameworks.coretests.R;
87
88import org.junit.Before;
89import org.junit.Rule;
90import org.junit.Test;
91import org.junit.runner.RunWith;
92
93/**
94 * Tests the TextView widget from an Activity
95 */
96@RunWith(AndroidJUnit4.class)
97@MediumTest
98public class TextViewActivityTest {
99
100    @Rule
101    public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>(
102            TextViewActivity.class);
103
104    private Activity mActivity;
105    private Instrumentation mInstrumentation;
106
107    @Before
108    public void setUp() {
109        mActivity = mActivityRule.getActivity();
110        mInstrumentation = InstrumentationRegistry.getInstrumentation();
111        mActivity.getSystemService(TextClassificationManager.class)
112                .setTextClassifier(TextClassifier.NO_OP);
113    }
114
115    @Test
116    public void testTypedTextIsOnScreen() {
117        final String helloWorld = "Hello world!";
118        // We use replaceText instead of typeTextIntoFocusedView to input text to avoid
119        // unintentional interactions with software keyboard.
120        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
121
122        onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
123    }
124    @Test
125    public void testPositionCursorAtTextAtIndex() {
126        final String helloWorld = "Hello world!";
127        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
128        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
129
130        // Delete text at specified index and see if we got the right one.
131        onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_FORWARD_DEL));
132        onView(withId(R.id.textview)).check(matches(withText("Hello orld!")));
133    }
134
135    @Test
136    public void testPositionCursorAtTextAtIndex_arabic() {
137        // Arabic text. The expected cursorable boundary is
138        // | \u0623 \u064F | \u067A | \u0633 \u0652 |
139        final String text = "\u0623\u064F\u067A\u0633\u0652";
140        onView(withId(R.id.textview)).perform(replaceText(text));
141
142        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
143        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
144        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(1));
145        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(anyOf(is(0), is(2))));
146        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(2));
147        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
148        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(3));
149        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(3));
150        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(4));
151        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(anyOf(is(3), is(5))));
152        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(5));
153        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(5));
154    }
155
156    @Test
157    public void testPositionCursorAtTextAtIndex_devanagari() {
158        // Devanagari text. The expected cursorable boundary is | \u0915 \u093E |
159        final String text = "\u0915\u093E";
160        onView(withId(R.id.textview)).perform(replaceText(text));
161
162        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
163        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
164        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(1));
165        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(anyOf(is(0), is(2))));
166        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(2));
167        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
168    }
169
170    @Test
171    public void testLongPressToSelect() {
172        final String helloWorld = "Hello Kirk!";
173        onView(withId(R.id.textview)).perform(click());
174        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
175        onView(withId(R.id.textview)).perform(
176                longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
177
178        onView(withId(R.id.textview)).check(hasSelection("Kirk"));
179    }
180
181    @Test
182    public void testLongPressEmptySpace() {
183        final String helloWorld = "Hello big round sun!";
184        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
185        // Move cursor somewhere else
186        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
187        // Long-press at end of line.
188        onView(withId(R.id.textview)).perform(longPressAtRelativeCoordinates(
189                RelativeCoordinatesProvider.HorizontalReference.RIGHT, -5,
190                RelativeCoordinatesProvider.VerticalReference.CENTER, 0));
191
192        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length()));
193    }
194
195    @Test
196    public void testLongPressAndDragToSelect() {
197        final String helloWorld = "Hello little handsome boy!";
198        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
199        onView(withId(R.id.textview)).perform(
200                longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
201
202        onView(withId(R.id.textview)).check(hasSelection("little handsome"));
203    }
204
205    @Test
206    public void testLongPressAndDragToSelect_emoji() {
207        final String text = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03";
208        onView(withId(R.id.textview)).perform(replaceText(text));
209
210        onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 6));
211        onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE02"));
212
213        onView(withId(R.id.textview)).perform(click());
214
215        onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 2));
216        onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE01"));
217    }
218
219    @Test
220    public void testDragAndDrop() {
221        final String text = "abc def ghi.";
222        onView(withId(R.id.textview)).perform(replaceText(text));
223        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
224
225        onView(withId(R.id.textview)).perform(
226                longPressAndDragOnText(text.indexOf("e"), text.length()));
227
228        onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
229        onView(withId(R.id.textview)).check(hasSelection(""));
230        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
231
232        // Test undo returns to the original state.
233        onView(withId(R.id.textview)).perform(pressKey(
234                (new EspressoKey.Builder()).withCtrlPressed(true).withKeyCode(KeyEvent.KEYCODE_Z)
235                        .build()));
236        onView(withId(R.id.textview)).check(matches(withText(text)));
237    }
238
239    @Test
240    public void testDoubleTapToSelect() {
241        final String helloWorld = "Hello SuetYi!";
242        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
243
244        onView(withId(R.id.textview)).perform(
245                doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
246
247        onView(withId(R.id.textview)).check(hasSelection("SuetYi"));
248    }
249
250    @Test
251    public void testDoubleTapAndDragToSelect() {
252        final String helloWorld = "Hello young beautiful person!";
253        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
254        onView(withId(R.id.textview)).perform(doubleTapAndDragOnText(helloWorld.indexOf("young"),
255                        helloWorld.indexOf(" person!")));
256
257        onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
258    }
259
260    @Test
261    public void testDoubleTapAndDragToSelect_multiLine() {
262        final String helloWorld = "abcd\n" + "efg\n" + "hijklm\n" + "nop";
263        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
264        onView(withId(R.id.textview)).perform(
265                doubleTapAndDragOnText(helloWorld.indexOf("m"), helloWorld.indexOf("a")));
266        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijklm"));
267    }
268
269    @Test
270    public void testSelectBackwordsByTouch() {
271        final String helloWorld = "Hello king of the Jungle!";
272        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
273        onView(withId(R.id.textview)).perform(
274                doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
275
276        onView(withId(R.id.textview)).check(hasSelection("king of the"));
277    }
278
279    @Test
280    public void testToolbarAppearsAfterSelection() {
281        final String text = "Toolbar appears after selection.";
282        onView(withId(R.id.textview)).perform(replaceText(text));
283        onView(withId(R.id.textview)).perform(
284                longPressOnTextAtIndex(text.indexOf("appears")));
285
286        sleepForFloatingToolbarPopup();
287        assertFloatingToolbarIsDisplayed();
288    }
289
290    @Test
291    public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint()
292            throws Throwable {
293        // after the hint layout change, the floating toolbar was not visible in the case below
294        // this test tests that the floating toolbar is displayed on the screen and is visible to
295        // user.
296        mActivityRule.runOnUiThread(() -> {
297            final TextView textView = mActivity.findViewById(R.id.textview);
298            textView.setTextDirection(TextView.TEXT_DIRECTION_FIRST_STRONG_LTR);
299            textView.setInputType(InputType.TYPE_CLASS_TEXT);
300            textView.setSingleLine(true);
301            textView.setHint("الروبوت");
302        });
303        mInstrumentation.waitForIdleSync();
304
305        onView(withId(R.id.textview)).perform(replaceText("test"));
306        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
307        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
308        onView(withId(R.id.textview)).perform(longClick());
309        sleepForFloatingToolbarPopup();
310
311        assertFloatingToolbarIsDisplayed();
312    }
313
314    @Test
315    public void testToolbarAppearsAfterLinkClicked() throws Throwable {
316        TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
317        int position = (textLink.getStart() + textLink.getEnd()) / 2;
318        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
319        sleepForFloatingToolbarPopup();
320        assertFloatingToolbarIsDisplayed();
321    }
322
323    @Test
324    public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable {
325        final TextView textView = mActivity.findViewById(R.id.nonselectable_textview);
326        final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
327        final int position = (textLink.getStart() + textLink.getEnd()) / 2;
328
329        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
330        sleepForFloatingToolbarPopup();
331        assertFloatingToolbarIsDisplayed();
332        assertTrue(textView.hasSelection());
333
334        // toggle
335        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
336        sleepForFloatingToolbarPopup();
337        assertFalse(textView.hasSelection());
338
339        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
340        sleepForFloatingToolbarPopup();
341        assertFloatingToolbarIsDisplayed();
342        assertTrue(textView.hasSelection());
343
344        // click outside
345        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(0));
346        sleepForFloatingToolbarPopup();
347        assertFalse(textView.hasSelection());
348    }
349
350    @Test
351    public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable {
352        final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
353        final int position = (textLink.getStart() + textLink.getEnd()) / 2;
354        final TextView textView = mActivity.findViewById(R.id.nonselectable_textview);
355        mActivityRule.runOnUiThread(() -> textView.setFocusableInTouchMode(true));
356
357        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
358        sleepForFloatingToolbarPopup();
359        assertFloatingToolbarIsDisplayed();
360        assertTrue(textView.hasSelection());
361
362        mActivityRule.runOnUiThread(() -> textView.clearFocus());
363        mInstrumentation.waitForIdleSync();
364        sleepForFloatingToolbarPopup();
365
366        assertFalse(textView.hasSelection());
367    }
368
369    @Test
370    public void testSelectionRemovedFromNonselectableTextWhenWindowLosesFocus() throws Throwable {
371        TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
372        int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
373        TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview);
374
375        onView(withId(R.id.nonselectable_textview))
376                .perform(clickOnTextAtIndex(nonselectablePosition));
377        sleepForFloatingToolbarPopup();
378        assertFloatingToolbarIsDisplayed();
379        assertTrue(nonselectableTextView.hasSelection());
380
381        UiDevice device = UiDevice.getInstance(mInstrumentation);
382        device.openNotification();
383        Thread.sleep(2000);
384        device.pressBack();
385        Thread.sleep(2000);
386
387        assertFalse(nonselectableTextView.hasSelection());
388    }
389
390    private TextLinks.TextLink addLinkifiedTextToTextView(int id) throws Throwable {
391        TextView textView = mActivity.findViewById(id);
392        useSystemDefaultTextClassifier();
393        TextClassificationManager textClassificationManager =
394                mActivity.getSystemService(TextClassificationManager.class);
395        TextClassifier textClassifier = textClassificationManager.getTextClassifier();
396        Spannable content = new SpannableString("Call me at +19148277737");
397        TextLinks.Request request = new TextLinks.Request.Builder(content).build();
398        TextLinks links = textClassifier.generateLinks(request);
399        TextLinksParams applyParams = new TextLinksParams.Builder()
400                .setApplyStrategy(TextLinks.APPLY_STRATEGY_REPLACE)
401                .build();
402        applyParams.apply(content, links);
403
404        mActivityRule.runOnUiThread(() -> {
405            textView.setText(content);
406            textView.setMovementMethod(LinkMovementMethod.getInstance());
407        });
408        mInstrumentation.waitForIdleSync();
409
410        // Wait for the UI thread to refresh
411        Thread.sleep(1000);
412
413        return links.getLinks().iterator().next();
414    }
415
416    @Test
417    public void testToolbarAndInsertionHandle() {
418        final String text = "text";
419        onView(withId(R.id.textview)).perform(replaceText(text));
420        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
421
422        onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
423        sleepForFloatingToolbarPopup();
424        assertFloatingToolbarIsDisplayed();
425
426        assertFloatingToolbarContainsItem(
427                mActivity.getString(com.android.internal.R.string.selectAll));
428        assertFloatingToolbarDoesNotContainItem(
429                mActivity.getString(com.android.internal.R.string.copy));
430        assertFloatingToolbarDoesNotContainItem(
431                mActivity.getString(com.android.internal.R.string.cut));
432    }
433
434    @Test
435    public void testToolbarAndSelectionHandle() {
436        final String text = "abcd efg hijk";
437        onView(withId(R.id.textview)).perform(replaceText(text));
438
439        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
440        sleepForFloatingToolbarPopup();
441        assertFloatingToolbarIsDisplayed();
442
443        assertFloatingToolbarContainsItem(
444                mActivity.getString(com.android.internal.R.string.selectAll));
445        assertFloatingToolbarContainsItem(
446                mActivity.getString(com.android.internal.R.string.copy));
447        assertFloatingToolbarContainsItem(
448                mActivity.getString(com.android.internal.R.string.cut));
449
450        final TextView textView = mActivity.findViewById(R.id.textview);
451        onHandleView(com.android.internal.R.id.selection_start_handle)
452                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
453        sleepForFloatingToolbarPopup();
454        assertFloatingToolbarIsDisplayed();
455
456        onHandleView(com.android.internal.R.id.selection_end_handle)
457                .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
458        sleepForFloatingToolbarPopup();
459        assertFloatingToolbarIsDisplayed();
460
461        assertFloatingToolbarDoesNotContainItem(
462                mActivity.getString(com.android.internal.R.string.selectAll));
463        assertFloatingToolbarContainsItem(
464                mActivity.getString(com.android.internal.R.string.copy));
465        assertFloatingToolbarContainsItem(
466                mActivity.getString(com.android.internal.R.string.cut));
467    }
468
469    @Test
470    public void testInsertionHandle() {
471        final String text = "abcd efg hijk ";
472        onView(withId(R.id.textview)).perform(replaceText(text));
473
474        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
475        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
476
477        final TextView textView = mActivity.findViewById(R.id.textview);
478
479        onHandleView(com.android.internal.R.id.insertion_handle)
480                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
481        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
482
483        onHandleView(com.android.internal.R.id.insertion_handle)
484                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
485        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
486    }
487
488    @Test
489    public void testInsertionHandle_multiLine() {
490        final String text = "abcd\n" + "efg\n" + "hijk\n";
491        onView(withId(R.id.textview)).perform(replaceText(text));
492
493        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
494        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
495
496        final TextView textView = mActivity.findViewById(R.id.textview);
497
498        onHandleView(com.android.internal.R.id.insertion_handle)
499                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
500        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
501
502        onHandleView(com.android.internal.R.id.insertion_handle)
503                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
504        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
505    }
506
507    @Test
508    public void testSelectionHandles() {
509        final String text = "abcd efg hijk lmn";
510        onView(withId(R.id.textview)).perform(replaceText(text));
511
512        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
513
514        onHandleView(com.android.internal.R.id.selection_start_handle)
515                .check(matches(isDisplayed()));
516        onHandleView(com.android.internal.R.id.selection_end_handle)
517                .check(matches(isDisplayed()));
518
519        final TextView textView = mActivity.findViewById(R.id.textview);
520        onHandleView(com.android.internal.R.id.selection_start_handle)
521                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
522        onView(withId(R.id.textview)).check(hasSelection("abcd efg"));
523
524        onHandleView(com.android.internal.R.id.selection_end_handle)
525                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('k') + 1));
526        onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk"));
527    }
528
529    @Test
530    public void testSelectionHandles_bidi() {
531        final String text = "abc \u0621\u0622\u0623 def";
532        onView(withId(R.id.textview)).perform(replaceText(text));
533
534        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622')));
535
536        onHandleView(com.android.internal.R.id.selection_start_handle)
537                .check(matches(isDisplayed()));
538        onHandleView(com.android.internal.R.id.selection_end_handle)
539                .check(matches(isDisplayed()));
540
541        onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
542
543        final TextView textView = mActivity.findViewById(R.id.textview);
544        onHandleView(com.android.internal.R.id.selection_start_handle)
545                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f')));
546        onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
547
548        onHandleView(com.android.internal.R.id.selection_end_handle)
549                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a')));
550        onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
551
552        onHandleView(com.android.internal.R.id.selection_start_handle)
553                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u0623'),
554                        false));
555        onView(withId(R.id.textview)).check(hasSelection("\u0623"));
556
557        onHandleView(com.android.internal.R.id.selection_start_handle)
558                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u0621'),
559                        false));
560        onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
561
562        onHandleView(com.android.internal.R.id.selection_start_handle)
563                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
564        onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623"));
565
566        onHandleView(com.android.internal.R.id.selection_end_handle)
567                .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
568        onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623 def"));
569    }
570
571    @Test
572    public void testSelectionHandles_multiLine() {
573        final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
574        onView(withId(R.id.textview)).perform(replaceText(text));
575        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
576
577        final TextView textView = mActivity.findViewById(R.id.textview);
578        onHandleView(com.android.internal.R.id.selection_start_handle)
579                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('e')));
580        onView(withId(R.id.textview)).check(hasSelection("efg\nhijk"));
581
582        onHandleView(com.android.internal.R.id.selection_start_handle)
583                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
584        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk"));
585
586        onHandleView(com.android.internal.R.id.selection_end_handle)
587                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('n') + 1));
588        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn"));
589
590        onHandleView(com.android.internal.R.id.selection_end_handle)
591                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('r') + 1));
592        onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr"));
593    }
594
595    @Suppress // Consistently failing.
596    @Test
597    public void testSelectionHandles_multiLine_rtl() {
598        // Arabic text.
599        final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
600                + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
601                + "\u0639\u063A\u063B";
602        onView(withId(R.id.textview)).perform(replaceText(text));
603        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0634')));
604
605        final TextView textView = mActivity.findViewById(R.id.textview);
606        onHandleView(com.android.internal.R.id.selection_start_handle)
607                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062E')));
608        onView(withId(R.id.textview)).check(hasSelection(
609                text.substring(text.indexOf('\u062D'), text.indexOf('\u0635') + 1)));
610
611        onHandleView(com.android.internal.R.id.selection_start_handle)
612                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062A')));
613        onView(withId(R.id.textview)).check(hasSelection(
614                text.substring(text.indexOf('\u062A'), text.indexOf('\u0635') + 1)));
615
616        onHandleView(com.android.internal.R.id.selection_end_handle)
617                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u0638')));
618        onView(withId(R.id.textview)).check(hasSelection(
619                text.substring(text.indexOf('\u062A'), text.indexOf('\u0638') + 1)));
620
621        onHandleView(com.android.internal.R.id.selection_end_handle)
622                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u063B')));
623        onView(withId(R.id.textview)).check(hasSelection(text));
624    }
625
626    @Test
627    public void testSelectionHandles_doesNotPassAnotherHandle() {
628        final String text = "abcd efg hijk lmn";
629        onView(withId(R.id.textview)).perform(replaceText(text));
630        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
631
632        final TextView textView = mActivity.findViewById(R.id.textview);
633        onHandleView(com.android.internal.R.id.selection_start_handle)
634                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('l')));
635        onView(withId(R.id.textview)).check(hasSelection("g"));
636
637        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
638        onHandleView(com.android.internal.R.id.selection_end_handle)
639                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a')));
640        onView(withId(R.id.textview)).check(hasSelection("e"));
641    }
642
643    @Test
644    public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() {
645        final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
646        onView(withId(R.id.textview)).perform(replaceText(text));
647        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
648
649        final TextView textView = mActivity.findViewById(R.id.textview);
650        onHandleView(com.android.internal.R.id.selection_start_handle)
651                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('r') + 1));
652        onView(withId(R.id.textview)).check(hasSelection("k"));
653
654        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
655        onHandleView(com.android.internal.R.id.selection_end_handle)
656                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a')));
657        onView(withId(R.id.textview)).check(hasSelection("h"));
658    }
659
660    @Test
661    public void testSelectionHandles_snapToWordBoundary() {
662        final String text = "abcd efg hijk lmn opqr";
663        onView(withId(R.id.textview)).perform(replaceText(text));
664        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
665
666        final TextView textView = mActivity.findViewById(R.id.textview);
667
668        onHandleView(com.android.internal.R.id.selection_start_handle)
669                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f')));
670        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
671
672        onHandleView(com.android.internal.R.id.selection_start_handle)
673                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('d') + 1));
674        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
675
676
677        onHandleView(com.android.internal.R.id.selection_start_handle)
678                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c')));
679        onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk"));
680
681        onHandleView(com.android.internal.R.id.selection_start_handle)
682                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('d')));
683        onView(withId(R.id.textview)).check(hasSelection("d efg hijk"));
684
685        onHandleView(com.android.internal.R.id.selection_start_handle)
686                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('b')));
687        onView(withId(R.id.textview)).check(hasSelection("bcd efg hijk"));
688
689        onView(withId(R.id.textview)).perform(click());
690        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
691
692        onHandleView(com.android.internal.R.id.selection_end_handle)
693                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('n')));
694        onView(withId(R.id.textview)).check(hasSelection("hijk lmn"));
695
696        onHandleView(com.android.internal.R.id.selection_end_handle)
697                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('o')));
698        onView(withId(R.id.textview)).check(hasSelection("hijk lmn"));
699
700        onHandleView(com.android.internal.R.id.selection_end_handle)
701                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('q')));
702        onView(withId(R.id.textview)).check(hasSelection("hijk lmn opqr"));
703
704        onHandleView(com.android.internal.R.id.selection_end_handle)
705                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('p')));
706        onView(withId(R.id.textview)).check(hasSelection("hijk lmn o"));
707
708        onHandleView(com.android.internal.R.id.selection_end_handle)
709                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('r')));
710        onView(withId(R.id.textview)).check(hasSelection("hijk lmn opq"));
711    }
712
713    @Test
714    public void testSelectionHandles_snapToWordBoundary_multiLine() {
715        final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu";
716        onView(withId(R.id.textview)).perform(replaceText(text));
717        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('m')));
718
719        final TextView textView = mActivity.findViewById(R.id.textview);
720
721        onHandleView(com.android.internal.R.id.selection_start_handle)
722                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c')));
723        onView(withId(R.id.textview)).check(hasSelection("abcd efg\nhijk lmn"));
724
725        onHandleView(com.android.internal.R.id.selection_start_handle)
726                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('g')));
727        onView(withId(R.id.textview)).check(hasSelection("g\nhijk lmn"));
728
729        onHandleView(com.android.internal.R.id.selection_start_handle)
730                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('m')));
731        onView(withId(R.id.textview)).check(hasSelection("lmn"));
732
733        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
734
735        onHandleView(com.android.internal.R.id.selection_end_handle)
736                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('u')));
737        onView(withId(R.id.textview)).check(hasSelection("hijk lmn\nopqr stu"));
738
739        onHandleView(com.android.internal.R.id.selection_end_handle)
740                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('p')));
741        onView(withId(R.id.textview)).check(hasSelection("hijk lmn\no"));
742
743        onHandleView(com.android.internal.R.id.selection_end_handle)
744                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
745        onView(withId(R.id.textview)).check(hasSelection("hijk"));
746    }
747
748    @Test
749    public void testSelectionHandles_visibleEvenWithEmptyMenu() {
750        ((TextView) mActivity.findViewById(R.id.textview)).setCustomSelectionActionModeCallback(
751                new ActionMode.Callback() {
752                    @Override
753                    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
754                        menu.clear();
755                        return true;
756                    }
757
758                    @Override
759                    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
760                        menu.clear();
761                        return true;
762                    }
763
764                    @Override
765                    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
766                        return false;
767                    }
768
769                    @Override
770                    public void onDestroyActionMode(ActionMode mode) {}
771                });
772        final String text = "abcd efg hijk lmn";
773        onView(withId(R.id.textview)).perform(replaceText(text));
774
775        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
776
777        onHandleView(com.android.internal.R.id.selection_start_handle)
778                .check(matches(isDisplayed()));
779        onHandleView(com.android.internal.R.id.selection_end_handle)
780                .check(matches(isDisplayed()));
781    }
782
783    @Test
784    public void testSetSelectionAndActionMode() throws Throwable {
785        final TextView textView = mActivity.findViewById(R.id.textview);
786        final ActionMode.Callback amCallback = mock(ActionMode.Callback.class);
787        when(amCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class)))
788                .thenReturn(true);
789        when(amCallback.onPrepareActionMode(any(ActionMode.class), any(Menu.class)))
790                .thenReturn(true);
791        textView.setCustomSelectionActionModeCallback(amCallback);
792
793        final String text = "abc def";
794        onView(withId(R.id.textview)).perform(replaceText(text));
795        mActivityRule.runOnUiThread(
796                () -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
797        mInstrumentation.waitForIdleSync();
798        // Don't automatically start action mode.
799        verify(amCallback, never()).onCreateActionMode(any(ActionMode.class), any(Menu.class));
800        // Make sure that "Select All" is included in the selection action mode when the entire text
801        // is not selected.
802        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
803        sleepForFloatingToolbarPopup();
804        assertFloatingToolbarIsDisplayed();
805        // Changing the selection range by API should not interrupt the selection action mode.
806        mActivityRule.runOnUiThread(
807                () -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
808        mInstrumentation.waitForIdleSync();
809        sleepForFloatingToolbarPopup();
810        assertFloatingToolbarIsDisplayed();
811        assertFloatingToolbarContainsItem(
812                mActivity.getString(com.android.internal.R.string.selectAll));
813        // Make sure that "Select All" is no longer included when the entire text is selected by
814        // API.
815        mActivityRule.runOnUiThread(
816                () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
817        mInstrumentation.waitForIdleSync();
818
819        sleepForFloatingToolbarPopup();
820        assertFloatingToolbarIsDisplayed();
821        assertFloatingToolbarDoesNotContainItem(
822                mActivity.getString(com.android.internal.R.string.selectAll));
823        // Make sure that shrinking the selection range to cursor (an empty range) by API
824        // terminates selection action mode and does not trigger the insertion action mode.
825        mActivityRule.runOnUiThread(
826                () -> Selection.setSelection((Spannable) textView.getText(), 0));
827        mInstrumentation.waitForIdleSync();
828
829        // Make sure that user click can trigger the insertion action mode.
830        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
831        onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
832        sleepForFloatingToolbarPopup();
833        assertFloatingToolbarIsDisplayed();
834        // Make sure that an existing insertion action mode keeps alive after the insertion point is
835        // moved by API.
836        mActivityRule.runOnUiThread(
837                () -> Selection.setSelection((Spannable) textView.getText(), 0));
838        mInstrumentation.waitForIdleSync();
839
840        sleepForFloatingToolbarPopup();
841        assertFloatingToolbarIsDisplayed();
842        assertFloatingToolbarDoesNotContainItem(
843                mActivity.getString(com.android.internal.R.string.copy));
844        // Make sure that selection action mode is started after selection is created by API when
845        // insertion action mode is active.
846        mActivityRule.runOnUiThread(
847                () -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
848        mInstrumentation.waitForIdleSync();
849
850        sleepForFloatingToolbarPopup();
851        assertFloatingToolbarIsDisplayed();
852        assertFloatingToolbarContainsItem(
853                mActivity.getString(com.android.internal.R.string.copy));
854    }
855
856    @Test
857    public void testTransientState() throws Throwable {
858        final String text = "abc def";
859        onView(withId(R.id.textview)).perform(replaceText(text));
860
861        final TextView textView = mActivity.findViewById(R.id.textview);
862        assertFalse(textView.hasTransientState());
863
864        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('b')));
865        // hasTransientState should return true when user generated selection is active.
866        assertTrue(textView.hasTransientState());
867        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('d')));
868        // hasTransientState should return false as the selection has been cleared.
869        assertFalse(textView.hasTransientState());
870        mActivityRule.runOnUiThread(
871                () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
872        mInstrumentation.waitForIdleSync();
873
874        // hasTransientState should return false when selection is created by API.
875        assertFalse(textView.hasTransientState());
876    }
877
878    @Test
879    public void testResetMenuItemTitle() throws Throwable {
880        mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
881        final TextView textView = mActivity.findViewById(R.id.textview);
882        final int itemId = 1;
883        final String title1 = " AFIGBO";
884        final int index = title1.indexOf('I');
885        final String title2 = title1.substring(index);
886        final String[] title = new String[]{title1};
887        mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
888                new ActionMode.Callback() {
889                    @Override
890                    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
891                        return true;
892                    }
893
894                    @Override
895                    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
896                        menu.removeItem(itemId);
897                        menu.add(Menu.NONE /* group */, itemId, 0 /* order */, title[0]);
898                        return true;
899                    }
900
901                    @Override
902                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
903                        return false;
904                    }
905
906                    @Override
907                    public void onDestroyActionMode(ActionMode actionMode) {
908                    }
909                }));
910        mInstrumentation.waitForIdleSync();
911
912        onView(withId(R.id.textview)).perform(replaceText(title1));
913        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(index));
914        sleepForFloatingToolbarPopup();
915        assertFloatingToolbarContainsItem(title1);
916
917        // Change the menu item title.
918        title[0] = title2;
919        // Change the selection to invalidate the action mode without restarting it.
920        onHandleView(com.android.internal.R.id.selection_start_handle)
921                .perform(dragHandle(textView, Handle.SELECTION_START, index));
922        sleepForFloatingToolbarPopup();
923        assertFloatingToolbarContainsItem(title2);
924    }
925
926    @Test
927    public void testAssistItemIsAtIndexZero() throws Throwable {
928        useSystemDefaultTextClassifier();
929        final TextView textView = mActivity.findViewById(R.id.textview);
930        mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
931                new ActionMode.Callback() {
932                    @Override
933                    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
934                        // Create another item at order position 0 to confirm that it will never be
935                        // placed before the textAssist item.
936                        menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test");
937                        return true;
938                    }
939
940                    @Override
941                    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
942                        return true;
943                    }
944
945                    @Override
946                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
947                        return false;
948                    }
949
950                    @Override
951                    public void onDestroyActionMode(ActionMode actionMode) {
952                    }
953                }));
954        mInstrumentation.waitForIdleSync();
955        final String text = "droid@android.com";
956
957        onView(withId(R.id.textview)).perform(replaceText(text));
958        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
959        sleepForFloatingToolbarPopup();
960        assertFloatingToolbarItemIndex(android.R.id.textAssist, 0);
961    }
962
963    @Test
964    public void testNoAssistItemForPasswordField() throws Throwable {
965        useSystemDefaultTextClassifier();
966        final TextView textView = mActivity.findViewById(R.id.textview);
967        mActivityRule.runOnUiThread(() -> {
968            textView.setInputType(
969                    InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
970        });
971        mInstrumentation.waitForIdleSync();
972        final String password = "afigbo@android.com";
973
974        onView(withId(R.id.textview)).perform(replaceText(password));
975        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@')));
976        sleepForFloatingToolbarPopup();
977        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
978    }
979
980    @Test
981    public void testPastePlainText_menuAction() {
982        initializeClipboardWithText(TextStyle.STYLED);
983
984        onView(withId(R.id.textview)).perform(replaceText(""));
985        onView(withId(R.id.textview)).perform(longClick());
986        sleepForFloatingToolbarPopup();
987        clickFloatingToolbarItem(
988                mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
989        mInstrumentation.waitForIdleSync();
990
991        onView(withId(R.id.textview)).check(matches(withText("styledtext")));
992        onView(withId(R.id.textview)).check(doesNotHaveStyledText());
993    }
994
995    @Test
996    public void testPastePlainText_noMenuItemForPlainText() {
997        initializeClipboardWithText(TextStyle.PLAIN);
998
999        onView(withId(R.id.textview)).perform(replaceText(""));
1000        onView(withId(R.id.textview)).perform(longClick());
1001        sleepForFloatingToolbarPopup();
1002
1003        assertFloatingToolbarDoesNotContainItem(
1004                mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
1005    }
1006
1007    private void useSystemDefaultTextClassifier() {
1008        mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
1009    }
1010
1011    private void initializeClipboardWithText(TextStyle textStyle) {
1012        final ClipData clip;
1013        switch (textStyle) {
1014            case STYLED:
1015                clip = ClipData.newHtmlText("html", "styledtext", "<b>styledtext</b>");
1016                break;
1017            case PLAIN:
1018                clip = ClipData.newPlainText("plain", "plaintext");
1019                break;
1020            default:
1021                throw new IllegalArgumentException("Invalid text style");
1022        }
1023        mActivity.getWindow().getDecorView().post(() ->
1024                mActivity.getSystemService(ClipboardManager.class).setPrimaryClip(clip));
1025        mInstrumentation.waitForIdleSync();
1026    }
1027
1028    private enum TextStyle {
1029        PLAIN, STYLED
1030    }
1031}
1032