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