TextViewActivityTest.java revision 080c8542b68cf17a0441862c404cb49ce0e86cfe
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 324 @Test 325 public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable { 326 TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview); 327 int position = (textLink.getStart() + textLink.getEnd()) / 2; 328 onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position)); 329 sleepForFloatingToolbarPopup(); 330 assertFloatingToolbarIsDisplayed(); 331 } 332 333 @Test 334 public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable { 335 // Add a link to both selectable and nonselectable TextViews: 336 TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview); 337 int selectablePosition = (textLink.getStart() + textLink.getEnd()) / 2; 338 textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview); 339 int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2; 340 TextView selectableTextView = mActivity.findViewById(R.id.textview); 341 TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview); 342 343 onView(withId(R.id.nonselectable_textview)) 344 .perform(clickOnTextAtIndex(nonselectablePosition)); 345 sleepForFloatingToolbarPopup(); 346 assertFloatingToolbarIsDisplayed(); 347 assertTrue(nonselectableTextView.hasSelection()); 348 349 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(selectablePosition)); 350 sleepForFloatingToolbarPopup(); 351 assertFloatingToolbarIsDisplayed(); 352 353 assertTrue(selectableTextView.hasSelection()); 354 assertFalse(nonselectableTextView.hasSelection()); 355 } 356 357 @Test 358 public void testSelectionRemovedFromNonselectableTextWhenWindowLosesFocus() throws Throwable { 359 TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview); 360 int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2; 361 TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview); 362 363 onView(withId(R.id.nonselectable_textview)) 364 .perform(clickOnTextAtIndex(nonselectablePosition)); 365 sleepForFloatingToolbarPopup(); 366 assertFloatingToolbarIsDisplayed(); 367 assertTrue(nonselectableTextView.hasSelection()); 368 369 UiDevice device = UiDevice.getInstance(mInstrumentation); 370 device.openNotification(); 371 Thread.sleep(2000); 372 device.pressBack(); 373 Thread.sleep(2000); 374 375 assertFalse(nonselectableTextView.hasSelection()); 376 } 377 378 private TextLinks.TextLink addLinkifiedTextToTextView(int id) throws Throwable { 379 TextView textView = mActivity.findViewById(id); 380 useSystemDefaultTextClassifier(); 381 TextClassificationManager textClassificationManager = 382 mActivity.getSystemService(TextClassificationManager.class); 383 TextClassifier textClassifier = textClassificationManager.getTextClassifier(); 384 Spannable content = new SpannableString("Call me at +19148277737"); 385 TextLinks.Request request = new TextLinks.Request.Builder(content).build(); 386 TextLinks links = textClassifier.generateLinks(request); 387 TextLinksParams applyParams = new TextLinksParams.Builder() 388 .setApplyStrategy(TextLinks.APPLY_STRATEGY_REPLACE) 389 .build(); 390 applyParams.apply(content, links); 391 392 mActivityRule.runOnUiThread(() -> { 393 textView.setText(content); 394 textView.setMovementMethod(LinkMovementMethod.getInstance()); 395 }); 396 mInstrumentation.waitForIdleSync(); 397 398 // Wait for the UI thread to refresh 399 Thread.sleep(1000); 400 401 return links.getLinks().iterator().next(); 402 } 403 404 @Test 405 public void testToolbarAndInsertionHandle() { 406 final String text = "text"; 407 onView(withId(R.id.textview)).perform(replaceText(text)); 408 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); 409 410 onHandleView(com.android.internal.R.id.insertion_handle).perform(click()); 411 sleepForFloatingToolbarPopup(); 412 assertFloatingToolbarIsDisplayed(); 413 414 assertFloatingToolbarContainsItem( 415 mActivity.getString(com.android.internal.R.string.selectAll)); 416 assertFloatingToolbarDoesNotContainItem( 417 mActivity.getString(com.android.internal.R.string.copy)); 418 assertFloatingToolbarDoesNotContainItem( 419 mActivity.getString(com.android.internal.R.string.cut)); 420 } 421 422 @Test 423 public void testToolbarAndSelectionHandle() { 424 final String text = "abcd efg hijk"; 425 onView(withId(R.id.textview)).perform(replaceText(text)); 426 427 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f"))); 428 sleepForFloatingToolbarPopup(); 429 assertFloatingToolbarIsDisplayed(); 430 431 assertFloatingToolbarContainsItem( 432 mActivity.getString(com.android.internal.R.string.selectAll)); 433 assertFloatingToolbarContainsItem( 434 mActivity.getString(com.android.internal.R.string.copy)); 435 assertFloatingToolbarContainsItem( 436 mActivity.getString(com.android.internal.R.string.cut)); 437 438 final TextView textView = mActivity.findViewById(R.id.textview); 439 onHandleView(com.android.internal.R.id.selection_start_handle) 440 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a'))); 441 sleepForFloatingToolbarPopup(); 442 assertFloatingToolbarIsDisplayed(); 443 444 onHandleView(com.android.internal.R.id.selection_end_handle) 445 .perform(dragHandle(textView, Handle.SELECTION_END, text.length())); 446 sleepForFloatingToolbarPopup(); 447 assertFloatingToolbarIsDisplayed(); 448 449 assertFloatingToolbarDoesNotContainItem( 450 mActivity.getString(com.android.internal.R.string.selectAll)); 451 assertFloatingToolbarContainsItem( 452 mActivity.getString(com.android.internal.R.string.copy)); 453 assertFloatingToolbarContainsItem( 454 mActivity.getString(com.android.internal.R.string.cut)); 455 } 456 457 @Test 458 public void testInsertionHandle() { 459 final String text = "abcd efg hijk "; 460 onView(withId(R.id.textview)).perform(replaceText(text)); 461 462 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); 463 onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length())); 464 465 final TextView textView = mActivity.findViewById(R.id.textview); 466 467 onHandleView(com.android.internal.R.id.insertion_handle) 468 .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a'))); 469 onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a"))); 470 471 onHandleView(com.android.internal.R.id.insertion_handle) 472 .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f'))); 473 onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f"))); 474 } 475 476 @Test 477 public void testInsertionHandle_multiLine() { 478 final String text = "abcd\n" + "efg\n" + "hijk\n"; 479 onView(withId(R.id.textview)).perform(replaceText(text)); 480 481 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); 482 onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length())); 483 484 final TextView textView = mActivity.findViewById(R.id.textview); 485 486 onHandleView(com.android.internal.R.id.insertion_handle) 487 .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a'))); 488 onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a"))); 489 490 onHandleView(com.android.internal.R.id.insertion_handle) 491 .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f'))); 492 onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f"))); 493 } 494 495 @Test 496 public void testSelectionHandles() { 497 final String text = "abcd efg hijk lmn"; 498 onView(withId(R.id.textview)).perform(replaceText(text)); 499 500 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f'))); 501 502 onHandleView(com.android.internal.R.id.selection_start_handle) 503 .check(matches(isDisplayed())); 504 onHandleView(com.android.internal.R.id.selection_end_handle) 505 .check(matches(isDisplayed())); 506 507 final TextView textView = mActivity.findViewById(R.id.textview); 508 onHandleView(com.android.internal.R.id.selection_start_handle) 509 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a'))); 510 onView(withId(R.id.textview)).check(hasSelection("abcd efg")); 511 512 onHandleView(com.android.internal.R.id.selection_end_handle) 513 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('k') + 1)); 514 onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk")); 515 } 516 517 @Test 518 public void testSelectionHandles_bidi() { 519 final String text = "abc \u0621\u0622\u0623 def"; 520 onView(withId(R.id.textview)).perform(replaceText(text)); 521 522 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622'))); 523 524 onHandleView(com.android.internal.R.id.selection_start_handle) 525 .check(matches(isDisplayed())); 526 onHandleView(com.android.internal.R.id.selection_end_handle) 527 .check(matches(isDisplayed())); 528 529 onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623")); 530 531 final TextView textView = mActivity.findViewById(R.id.textview); 532 onHandleView(com.android.internal.R.id.selection_start_handle) 533 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f'))); 534 onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623")); 535 536 onHandleView(com.android.internal.R.id.selection_end_handle) 537 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a'))); 538 onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623")); 539 540 onHandleView(com.android.internal.R.id.selection_start_handle) 541 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u0623'), 542 false)); 543 onView(withId(R.id.textview)).check(hasSelection("\u0623")); 544 545 onHandleView(com.android.internal.R.id.selection_start_handle) 546 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u0621'), 547 false)); 548 onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623")); 549 550 onHandleView(com.android.internal.R.id.selection_start_handle) 551 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a'))); 552 onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623")); 553 554 onHandleView(com.android.internal.R.id.selection_end_handle) 555 .perform(dragHandle(textView, Handle.SELECTION_END, text.length())); 556 onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623 def")); 557 } 558 559 @Test 560 public void testSelectionHandles_multiLine() { 561 final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr"; 562 onView(withId(R.id.textview)).perform(replaceText(text)); 563 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i'))); 564 565 final TextView textView = mActivity.findViewById(R.id.textview); 566 onHandleView(com.android.internal.R.id.selection_start_handle) 567 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('e'))); 568 onView(withId(R.id.textview)).check(hasSelection("efg\nhijk")); 569 570 onHandleView(com.android.internal.R.id.selection_start_handle) 571 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a'))); 572 onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk")); 573 574 onHandleView(com.android.internal.R.id.selection_end_handle) 575 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('n') + 1)); 576 onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn")); 577 578 onHandleView(com.android.internal.R.id.selection_end_handle) 579 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('r') + 1)); 580 onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr")); 581 } 582 583 @Suppress // Consistently failing. 584 @Test 585 public void testSelectionHandles_multiLine_rtl() { 586 // Arabic text. 587 final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n" 588 + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n" 589 + "\u0639\u063A\u063B"; 590 onView(withId(R.id.textview)).perform(replaceText(text)); 591 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0634'))); 592 593 final TextView textView = mActivity.findViewById(R.id.textview); 594 onHandleView(com.android.internal.R.id.selection_start_handle) 595 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062E'))); 596 onView(withId(R.id.textview)).check(hasSelection( 597 text.substring(text.indexOf('\u062D'), text.indexOf('\u0635') + 1))); 598 599 onHandleView(com.android.internal.R.id.selection_start_handle) 600 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062A'))); 601 onView(withId(R.id.textview)).check(hasSelection( 602 text.substring(text.indexOf('\u062A'), text.indexOf('\u0635') + 1))); 603 604 onHandleView(com.android.internal.R.id.selection_end_handle) 605 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u0638'))); 606 onView(withId(R.id.textview)).check(hasSelection( 607 text.substring(text.indexOf('\u062A'), text.indexOf('\u0638') + 1))); 608 609 onHandleView(com.android.internal.R.id.selection_end_handle) 610 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u063B'))); 611 onView(withId(R.id.textview)).check(hasSelection(text)); 612 } 613 614 @Test 615 public void testSelectionHandles_doesNotPassAnotherHandle() { 616 final String text = "abcd efg hijk lmn"; 617 onView(withId(R.id.textview)).perform(replaceText(text)); 618 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f'))); 619 620 final TextView textView = mActivity.findViewById(R.id.textview); 621 onHandleView(com.android.internal.R.id.selection_start_handle) 622 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('l'))); 623 onView(withId(R.id.textview)).check(hasSelection("g")); 624 625 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f'))); 626 onHandleView(com.android.internal.R.id.selection_end_handle) 627 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a'))); 628 onView(withId(R.id.textview)).check(hasSelection("e")); 629 } 630 631 @Test 632 public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() { 633 final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr"; 634 onView(withId(R.id.textview)).perform(replaceText(text)); 635 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i'))); 636 637 final TextView textView = mActivity.findViewById(R.id.textview); 638 onHandleView(com.android.internal.R.id.selection_start_handle) 639 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('r') + 1)); 640 onView(withId(R.id.textview)).check(hasSelection("k")); 641 642 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i'))); 643 onHandleView(com.android.internal.R.id.selection_end_handle) 644 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a'))); 645 onView(withId(R.id.textview)).check(hasSelection("h")); 646 } 647 648 @Test 649 public void testSelectionHandles_snapToWordBoundary() { 650 final String text = "abcd efg hijk lmn opqr"; 651 onView(withId(R.id.textview)).perform(replaceText(text)); 652 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i'))); 653 654 final TextView textView = mActivity.findViewById(R.id.textview); 655 656 onHandleView(com.android.internal.R.id.selection_start_handle) 657 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f'))); 658 onView(withId(R.id.textview)).check(hasSelection("efg hijk")); 659 660 onHandleView(com.android.internal.R.id.selection_start_handle) 661 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('d') + 1)); 662 onView(withId(R.id.textview)).check(hasSelection("efg hijk")); 663 664 665 onHandleView(com.android.internal.R.id.selection_start_handle) 666 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c'))); 667 onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk")); 668 669 onHandleView(com.android.internal.R.id.selection_start_handle) 670 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('d'))); 671 onView(withId(R.id.textview)).check(hasSelection("d efg hijk")); 672 673 onHandleView(com.android.internal.R.id.selection_start_handle) 674 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('b'))); 675 onView(withId(R.id.textview)).check(hasSelection("bcd efg hijk")); 676 677 onView(withId(R.id.textview)).perform(click()); 678 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i'))); 679 680 onHandleView(com.android.internal.R.id.selection_end_handle) 681 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('n'))); 682 onView(withId(R.id.textview)).check(hasSelection("hijk lmn")); 683 684 onHandleView(com.android.internal.R.id.selection_end_handle) 685 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('o'))); 686 onView(withId(R.id.textview)).check(hasSelection("hijk lmn")); 687 688 onHandleView(com.android.internal.R.id.selection_end_handle) 689 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('q'))); 690 onView(withId(R.id.textview)).check(hasSelection("hijk lmn opqr")); 691 692 onHandleView(com.android.internal.R.id.selection_end_handle) 693 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('p'))); 694 onView(withId(R.id.textview)).check(hasSelection("hijk lmn o")); 695 696 onHandleView(com.android.internal.R.id.selection_end_handle) 697 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('r'))); 698 onView(withId(R.id.textview)).check(hasSelection("hijk lmn opq")); 699 } 700 701 @Test 702 public void testSelectionHandles_snapToWordBoundary_multiLine() { 703 final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu"; 704 onView(withId(R.id.textview)).perform(replaceText(text)); 705 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('m'))); 706 707 final TextView textView = mActivity.findViewById(R.id.textview); 708 709 onHandleView(com.android.internal.R.id.selection_start_handle) 710 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c'))); 711 onView(withId(R.id.textview)).check(hasSelection("abcd efg\nhijk lmn")); 712 713 onHandleView(com.android.internal.R.id.selection_start_handle) 714 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('g'))); 715 onView(withId(R.id.textview)).check(hasSelection("g\nhijk lmn")); 716 717 onHandleView(com.android.internal.R.id.selection_start_handle) 718 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('m'))); 719 onView(withId(R.id.textview)).check(hasSelection("lmn")); 720 721 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i'))); 722 723 onHandleView(com.android.internal.R.id.selection_end_handle) 724 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('u'))); 725 onView(withId(R.id.textview)).check(hasSelection("hijk lmn\nopqr stu")); 726 727 onHandleView(com.android.internal.R.id.selection_end_handle) 728 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('p'))); 729 onView(withId(R.id.textview)).check(hasSelection("hijk lmn\no")); 730 731 onHandleView(com.android.internal.R.id.selection_end_handle) 732 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i'))); 733 onView(withId(R.id.textview)).check(hasSelection("hijk")); 734 } 735 736 @Test 737 public void testSelectionHandles_visibleEvenWithEmptyMenu() { 738 ((TextView) mActivity.findViewById(R.id.textview)).setCustomSelectionActionModeCallback( 739 new ActionMode.Callback() { 740 @Override 741 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 742 menu.clear(); 743 return true; 744 } 745 746 @Override 747 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 748 menu.clear(); 749 return true; 750 } 751 752 @Override 753 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 754 return false; 755 } 756 757 @Override 758 public void onDestroyActionMode(ActionMode mode) {} 759 }); 760 final String text = "abcd efg hijk lmn"; 761 onView(withId(R.id.textview)).perform(replaceText(text)); 762 763 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f'))); 764 765 onHandleView(com.android.internal.R.id.selection_start_handle) 766 .check(matches(isDisplayed())); 767 onHandleView(com.android.internal.R.id.selection_end_handle) 768 .check(matches(isDisplayed())); 769 } 770 771 @Test 772 public void testSetSelectionAndActionMode() throws Throwable { 773 final TextView textView = mActivity.findViewById(R.id.textview); 774 final ActionMode.Callback amCallback = mock(ActionMode.Callback.class); 775 when(amCallback.onCreateActionMode(any(ActionMode.class), any(Menu.class))) 776 .thenReturn(true); 777 when(amCallback.onPrepareActionMode(any(ActionMode.class), any(Menu.class))) 778 .thenReturn(true); 779 textView.setCustomSelectionActionModeCallback(amCallback); 780 781 final String text = "abc def"; 782 onView(withId(R.id.textview)).perform(replaceText(text)); 783 mActivityRule.runOnUiThread( 784 () -> Selection.setSelection((Spannable) textView.getText(), 0, 3)); 785 mInstrumentation.waitForIdleSync(); 786 // Don't automatically start action mode. 787 verify(amCallback, never()).onCreateActionMode(any(ActionMode.class), any(Menu.class)); 788 // Make sure that "Select All" is included in the selection action mode when the entire text 789 // is not selected. 790 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e'))); 791 sleepForFloatingToolbarPopup(); 792 assertFloatingToolbarIsDisplayed(); 793 // Changing the selection range by API should not interrupt the selection action mode. 794 mActivityRule.runOnUiThread( 795 () -> Selection.setSelection((Spannable) textView.getText(), 0, 3)); 796 mInstrumentation.waitForIdleSync(); 797 sleepForFloatingToolbarPopup(); 798 assertFloatingToolbarIsDisplayed(); 799 assertFloatingToolbarContainsItem( 800 mActivity.getString(com.android.internal.R.string.selectAll)); 801 // Make sure that "Select All" is no longer included when the entire text is selected by 802 // API. 803 mActivityRule.runOnUiThread( 804 () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length())); 805 mInstrumentation.waitForIdleSync(); 806 807 sleepForFloatingToolbarPopup(); 808 assertFloatingToolbarIsDisplayed(); 809 assertFloatingToolbarDoesNotContainItem( 810 mActivity.getString(com.android.internal.R.string.selectAll)); 811 // Make sure that shrinking the selection range to cursor (an empty range) by API 812 // terminates selection action mode and does not trigger the insertion action mode. 813 mActivityRule.runOnUiThread( 814 () -> Selection.setSelection((Spannable) textView.getText(), 0)); 815 mInstrumentation.waitForIdleSync(); 816 817 // Make sure that user click can trigger the insertion action mode. 818 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); 819 onHandleView(com.android.internal.R.id.insertion_handle).perform(click()); 820 sleepForFloatingToolbarPopup(); 821 assertFloatingToolbarIsDisplayed(); 822 // Make sure that an existing insertion action mode keeps alive after the insertion point is 823 // moved by API. 824 mActivityRule.runOnUiThread( 825 () -> Selection.setSelection((Spannable) textView.getText(), 0)); 826 mInstrumentation.waitForIdleSync(); 827 828 sleepForFloatingToolbarPopup(); 829 assertFloatingToolbarIsDisplayed(); 830 assertFloatingToolbarDoesNotContainItem( 831 mActivity.getString(com.android.internal.R.string.copy)); 832 // Make sure that selection action mode is started after selection is created by API when 833 // insertion action mode is active. 834 mActivityRule.runOnUiThread( 835 () -> Selection.setSelection((Spannable) textView.getText(), 1, text.length())); 836 mInstrumentation.waitForIdleSync(); 837 838 sleepForFloatingToolbarPopup(); 839 assertFloatingToolbarIsDisplayed(); 840 assertFloatingToolbarContainsItem( 841 mActivity.getString(com.android.internal.R.string.copy)); 842 } 843 844 @Test 845 public void testTransientState() throws Throwable { 846 final String text = "abc def"; 847 onView(withId(R.id.textview)).perform(replaceText(text)); 848 849 final TextView textView = mActivity.findViewById(R.id.textview); 850 assertFalse(textView.hasTransientState()); 851 852 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('b'))); 853 // hasTransientState should return true when user generated selection is active. 854 assertTrue(textView.hasTransientState()); 855 onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('d'))); 856 // hasTransientState should return false as the selection has been cleared. 857 assertFalse(textView.hasTransientState()); 858 mActivityRule.runOnUiThread( 859 () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length())); 860 mInstrumentation.waitForIdleSync(); 861 862 // hasTransientState should return false when selection is created by API. 863 assertFalse(textView.hasTransientState()); 864 } 865 866 @Test 867 public void testResetMenuItemTitle() throws Throwable { 868 mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null); 869 final TextView textView = mActivity.findViewById(R.id.textview); 870 final int itemId = 1; 871 final String title1 = " AFIGBO"; 872 final int index = title1.indexOf('I'); 873 final String title2 = title1.substring(index); 874 final String[] title = new String[]{title1}; 875 mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback( 876 new ActionMode.Callback() { 877 @Override 878 public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { 879 return true; 880 } 881 882 @Override 883 public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { 884 menu.removeItem(itemId); 885 menu.add(Menu.NONE /* group */, itemId, 0 /* order */, title[0]); 886 return true; 887 } 888 889 @Override 890 public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { 891 return false; 892 } 893 894 @Override 895 public void onDestroyActionMode(ActionMode actionMode) { 896 } 897 })); 898 mInstrumentation.waitForIdleSync(); 899 900 onView(withId(R.id.textview)).perform(replaceText(title1)); 901 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(index)); 902 sleepForFloatingToolbarPopup(); 903 assertFloatingToolbarContainsItem(title1); 904 905 // Change the menu item title. 906 title[0] = title2; 907 // Change the selection to invalidate the action mode without restarting it. 908 onHandleView(com.android.internal.R.id.selection_start_handle) 909 .perform(dragHandle(textView, Handle.SELECTION_START, index)); 910 sleepForFloatingToolbarPopup(); 911 assertFloatingToolbarContainsItem(title2); 912 } 913 914 @Test 915 public void testAssistItemIsAtIndexZero() throws Throwable { 916 useSystemDefaultTextClassifier(); 917 final TextView textView = mActivity.findViewById(R.id.textview); 918 mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback( 919 new ActionMode.Callback() { 920 @Override 921 public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { 922 // Create another item at order position 0 to confirm that it will never be 923 // placed before the textAssist item. 924 menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test"); 925 return true; 926 } 927 928 @Override 929 public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { 930 return true; 931 } 932 933 @Override 934 public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { 935 return false; 936 } 937 938 @Override 939 public void onDestroyActionMode(ActionMode actionMode) { 940 } 941 })); 942 mInstrumentation.waitForIdleSync(); 943 final String text = "droid@android.com"; 944 945 onView(withId(R.id.textview)).perform(replaceText(text)); 946 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@'))); 947 sleepForFloatingToolbarPopup(); 948 assertFloatingToolbarItemIndex(android.R.id.textAssist, 0); 949 } 950 951 @Test 952 public void testNoAssistItemForPasswordField() throws Throwable { 953 useSystemDefaultTextClassifier(); 954 final TextView textView = mActivity.findViewById(R.id.textview); 955 mActivityRule.runOnUiThread(() -> { 956 textView.setInputType( 957 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); 958 }); 959 mInstrumentation.waitForIdleSync(); 960 final String password = "afigbo@android.com"; 961 962 onView(withId(R.id.textview)).perform(replaceText(password)); 963 onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@'))); 964 sleepForFloatingToolbarPopup(); 965 assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist); 966 } 967 968 @Test 969 public void testPastePlainText_menuAction() { 970 initializeClipboardWithText(TextStyle.STYLED); 971 972 onView(withId(R.id.textview)).perform(replaceText("")); 973 onView(withId(R.id.textview)).perform(longClick()); 974 sleepForFloatingToolbarPopup(); 975 clickFloatingToolbarItem( 976 mActivity.getString(com.android.internal.R.string.paste_as_plain_text)); 977 mInstrumentation.waitForIdleSync(); 978 979 onView(withId(R.id.textview)).check(matches(withText("styledtext"))); 980 onView(withId(R.id.textview)).check(doesNotHaveStyledText()); 981 } 982 983 @Test 984 public void testPastePlainText_noMenuItemForPlainText() { 985 initializeClipboardWithText(TextStyle.PLAIN); 986 987 onView(withId(R.id.textview)).perform(replaceText("")); 988 onView(withId(R.id.textview)).perform(longClick()); 989 sleepForFloatingToolbarPopup(); 990 991 assertFloatingToolbarDoesNotContainItem( 992 mActivity.getString(com.android.internal.R.string.paste_as_plain_text)); 993 } 994 995 private void useSystemDefaultTextClassifier() { 996 mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null); 997 } 998 999 private void initializeClipboardWithText(TextStyle textStyle) { 1000 final ClipData clip; 1001 switch (textStyle) { 1002 case STYLED: 1003 clip = ClipData.newHtmlText("html", "styledtext", "<b>styledtext</b>"); 1004 break; 1005 case PLAIN: 1006 clip = ClipData.newPlainText("plain", "plaintext"); 1007 break; 1008 default: 1009 throw new IllegalArgumentException("Invalid text style"); 1010 } 1011 mActivity.getWindow().getDecorView().post(() -> 1012 mActivity.getSystemService(ClipboardManager.class).setPrimaryClip(clip)); 1013 mInstrumentation.waitForIdleSync(); 1014 } 1015 1016 private enum TextStyle { 1017 PLAIN, STYLED 1018 } 1019} 1020