1 /* 2 * Copyright (C) 2008 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.cts; 18 19 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertTrue; 27 28 import android.app.Activity; 29 import android.app.Instrumentation; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.res.Configuration; 33 import android.graphics.Point; 34 import android.os.SystemClock; 35 import android.support.test.uiautomator.By; 36 import android.support.test.uiautomator.UiDevice; 37 import android.support.test.uiautomator.UiObject2; 38 import android.support.test.uiautomator.Until; 39 import android.text.Editable; 40 import android.text.InputFilter; 41 import android.text.InputType; 42 import android.text.Layout; 43 import android.text.Spanned; 44 import android.text.TextUtils; 45 import android.text.method.ArrowKeyMovementMethod; 46 import android.text.method.MovementMethod; 47 import android.util.AttributeSet; 48 import android.util.DisplayMetrics; 49 import android.util.SparseArray; 50 import android.util.TypedValue; 51 import android.util.Xml; 52 import android.view.KeyEvent; 53 import android.view.ViewConfiguration; 54 import android.view.ViewGroup; 55 import android.view.accessibility.AccessibilityNodeInfo; 56 import android.view.inputmethod.EditorInfo; 57 import android.widget.EditText; 58 import android.widget.Editor; 59 import android.widget.LinearLayout; 60 import android.widget.TextView; 61 import android.widget.TextView.BufferType; 62 63 import androidx.test.InstrumentationRegistry; 64 import androidx.test.annotation.UiThreadTest; 65 import androidx.test.filters.SmallTest; 66 import androidx.test.rule.ActivityTestRule; 67 import androidx.test.runner.AndroidJUnit4; 68 69 import com.android.compatibility.common.util.CtsKeyEventUtil; 70 import com.android.compatibility.common.util.CtsTouchUtils; 71 import com.android.cts.mockime.ImeEventStream; 72 import com.android.cts.mockime.ImeSettings; 73 import com.android.cts.mockime.MockImeSession; 74 75 import org.junit.After; 76 import org.junit.Before; 77 import org.junit.Rule; 78 import org.junit.Test; 79 import org.junit.runner.RunWith; 80 import org.xmlpull.v1.XmlPullParser; 81 82 import java.util.List; 83 import java.util.concurrent.TimeUnit; 84 import java.util.concurrent.atomic.AtomicReference; 85 86 @SmallTest 87 @RunWith(AndroidJUnit4.class) 88 public class EditTextTest { 89 90 private Activity mActivity; 91 private EditText mEditText1; 92 private EditText mEditText2; 93 private AttributeSet mAttributeSet; 94 private Instrumentation mInstrumentation; 95 private CtsTouchUtils mCtsTouchUtils; 96 private CtsKeyEventUtil mCtsKeyEventUtil; 97 98 @Rule 99 public ActivityTestRule<EditTextCtsActivity> mActivityRule = 100 new ActivityTestRule<>(EditTextCtsActivity.class); 101 public ActivityTestRule<EditTextCursorCtsActivity> mEmptyActivityRule = 102 new ActivityTestRule<>(EditTextCursorCtsActivity.class, false, false); 103 104 @Before setup()105 public void setup() { 106 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 107 mCtsTouchUtils = new CtsTouchUtils(mInstrumentation.getTargetContext()); 108 mCtsKeyEventUtil = new CtsKeyEventUtil(mInstrumentation.getTargetContext()); 109 mActivity = mActivityRule.getActivity(); 110 mEditText1 = (EditText) mActivity.findViewById(R.id.edittext_simple1); 111 mEditText2 = (EditText) mActivity.findViewById(R.id.edittext_simple2); 112 113 XmlPullParser parser = mActivity.getResources().getXml(R.layout.edittext_layout); 114 mAttributeSet = Xml.asAttributeSet(parser); 115 } 116 117 @After teardown()118 public void teardown() throws Throwable { 119 mActivityRule.runOnUiThread(() -> mEditText1.setSingleLine(false)); 120 } 121 122 @Test testConstructor()123 public void testConstructor() { 124 new EditText(mActivity); 125 126 new EditText(mActivity, null); 127 128 new EditText(mActivity, null, 0); 129 130 new EditText(mActivity, mAttributeSet); 131 132 new EditText(mActivity, mAttributeSet, 0); 133 } 134 135 @Test(expected=NullPointerException.class) testConstructorNullContext1()136 public void testConstructorNullContext1() { 137 new EditText(null); 138 } 139 140 @Test(expected=NullPointerException.class) testConstructorNullContext2()141 public void testConstructorNullContext2() { 142 new EditText(null, null); 143 } 144 145 @UiThreadTest 146 @Test testAccessText()147 public void testAccessText() { 148 mEditText1.setText("android", BufferType.NORMAL); 149 assertTrue(TextUtils.equals("android", mEditText1.getText())); 150 151 mEditText1.setText("", BufferType.SPANNABLE); 152 assertEquals(0, mEditText1.getText().length()); 153 154 mEditText1.setText(null, BufferType.EDITABLE); 155 assertEquals(0, mEditText1.getText().length()); 156 } 157 158 @UiThreadTest 159 @Test testSetSelectionIndex()160 public void testSetSelectionIndex() { 161 mEditText1.setText("android", BufferType.EDITABLE); 162 int position = 4; 163 mEditText1.setSelection(position); 164 assertEquals(position, mEditText1.getSelectionStart()); 165 assertEquals(position, mEditText1.getSelectionEnd()); 166 167 position = 0; 168 mEditText1.setSelection(position); 169 assertEquals(position, mEditText1.getSelectionStart()); 170 assertEquals(position, mEditText1.getSelectionEnd()); 171 } 172 173 @UiThreadTest 174 @Test(expected=IndexOutOfBoundsException.class) testSetSelectionIndexBeforeFirst()175 public void testSetSelectionIndexBeforeFirst() { 176 mEditText1.setText("android", BufferType.EDITABLE); 177 mEditText1.setSelection(-1); 178 } 179 180 @UiThreadTest 181 @Test(expected=IndexOutOfBoundsException.class) testSetSelectionIndexAfterLast()182 public void testSetSelectionIndexAfterLast() { 183 mEditText1.setText("android", BufferType.EDITABLE); 184 mEditText1.setSelection(mEditText1.getText().length() + 1); 185 } 186 187 @UiThreadTest 188 @Test testSetSelectionStartEnd()189 public void testSetSelectionStartEnd() { 190 mEditText1.setText("android", BufferType.EDITABLE); 191 int start = 1; 192 int end = 2; 193 mEditText1.setSelection(start, end); 194 assertEquals(start, mEditText1.getSelectionStart()); 195 assertEquals(end, mEditText1.getSelectionEnd()); 196 197 start = 0; 198 end = 0; 199 mEditText1.setSelection(start, end); 200 assertEquals(start, mEditText1.getSelectionStart()); 201 assertEquals(end, mEditText1.getSelectionEnd()); 202 203 start = 7; 204 end = 1; 205 mEditText1.setSelection(start, end); 206 assertEquals(start, mEditText1.getSelectionStart()); 207 assertEquals(end, mEditText1.getSelectionEnd()); 208 } 209 210 @UiThreadTest 211 @Test(expected=IndexOutOfBoundsException.class) testSetSelectionStartEndBeforeFirst()212 public void testSetSelectionStartEndBeforeFirst() { 213 mEditText1.setText("android", BufferType.EDITABLE); 214 mEditText1.setSelection(-5, -1); 215 } 216 217 @UiThreadTest 218 @Test(expected=IndexOutOfBoundsException.class) testSetSelectionStartEndAfterLast()219 public void testSetSelectionStartEndAfterLast() { 220 mEditText1.setText("android", BufferType.EDITABLE); 221 mEditText1.setSelection(5, mEditText1.getText().length() + 1); 222 } 223 224 @UiThreadTest 225 @Test testSelectAll()226 public void testSelectAll() { 227 String string = "android"; 228 mEditText1.setText(string, BufferType.EDITABLE); 229 mEditText1.selectAll(); 230 assertEquals(0, mEditText1.getSelectionStart()); 231 assertEquals(string.length(), mEditText1.getSelectionEnd()); 232 233 mEditText1.setText("", BufferType.EDITABLE); 234 mEditText1.selectAll(); 235 assertEquals(0, mEditText1.getSelectionStart()); 236 assertEquals(0, mEditText1.getSelectionEnd()); 237 238 mEditText1.setText(null, BufferType.EDITABLE); 239 mEditText1.selectAll(); 240 assertEquals(0, mEditText1.getSelectionStart()); 241 assertEquals(0, mEditText1.getSelectionEnd()); 242 } 243 244 @UiThreadTest 245 @Test testExtendSelection()246 public void testExtendSelection() { 247 mEditText1.setText("android", BufferType.EDITABLE); 248 int start = 0; 249 int end = 0; 250 mEditText1.setSelection(start, end); 251 assertEquals(start, mEditText1.getSelectionStart()); 252 assertEquals(end, mEditText1.getSelectionEnd()); 253 254 end = 6; 255 mEditText1.extendSelection(end); 256 assertEquals(start, mEditText1.getSelectionStart()); 257 assertEquals(end, mEditText1.getSelectionEnd()); 258 259 start = 0; 260 end = 0; 261 mEditText1.setSelection(start); 262 mEditText1.extendSelection(end); 263 assertEquals(start, mEditText1.getSelectionStart()); 264 assertEquals(end, mEditText1.getSelectionEnd()); 265 } 266 267 @UiThreadTest 268 @Test(expected=IndexOutOfBoundsException.class) testExtendSelectionBeyondLast()269 public void testExtendSelectionBeyondLast() { 270 mEditText1.setText("android", BufferType.EDITABLE); 271 mEditText1.setSelection(0, 4); 272 mEditText1.extendSelection(10); 273 } 274 275 @Test testGetDefaultEditable()276 public void testGetDefaultEditable() { 277 MockEditText mockEditText = new MockEditText(mActivity, mAttributeSet); 278 279 assertTrue(mockEditText.getDefaultEditable()); 280 } 281 282 @Test testAutoSizeNotSupported()283 public void testAutoSizeNotSupported() { 284 DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics(); 285 EditText autoSizeEditText = (EditText) mActivity.findViewById(R.id.edittext_autosize); 286 287 // If auto-size would work then the text size would be less then 50dp (the value set in the 288 // layout file). 289 final int sizeSetInPixels = (int) (0.5f + TypedValue.applyDimension( 290 TypedValue.COMPLEX_UNIT_DIP, 50f, metrics)); 291 assertEquals(sizeSetInPixels, (int) autoSizeEditText.getTextSize()); 292 } 293 294 @Test testGetDefaultMovementMethod()295 public void testGetDefaultMovementMethod() { 296 MockEditText mockEditText = new MockEditText(mActivity, mAttributeSet); 297 MovementMethod method1 = mockEditText.getDefaultMovementMethod(); 298 MovementMethod method2 = mockEditText.getDefaultMovementMethod(); 299 300 assertNotNull(method1); 301 assertTrue(method1 instanceof ArrowKeyMovementMethod); 302 303 assertSame(method1, method2); 304 } 305 306 @UiThreadTest 307 @Test testSetEllipsize()308 public void testSetEllipsize() { 309 assertNull(mEditText1.getEllipsize()); 310 311 mEditText1.setEllipsize(TextUtils.TruncateAt.START); 312 assertSame(TextUtils.TruncateAt.START, mEditText1.getEllipsize()); 313 } 314 315 @UiThreadTest 316 @Test(expected=IllegalArgumentException.class) testSetEllipsizeMarquee()317 public void testSetEllipsizeMarquee() { 318 mEditText1.setEllipsize(TextUtils.TruncateAt.MARQUEE); 319 } 320 321 @UiThreadTest 322 @Test testOnSaveInstanceState_savesTextStateWhenFreezesTextIsTrue()323 public void testOnSaveInstanceState_savesTextStateWhenFreezesTextIsTrue() { 324 // prepare EditText for before saveInstanceState 325 final String testStr = "This is a test str"; 326 mEditText1.setFreezesText(true); 327 mEditText1.setText(testStr); 328 329 // prepare EditText for after saveInstanceState 330 mEditText2.setFreezesText(true); 331 332 mEditText2.onRestoreInstanceState(mEditText1.onSaveInstanceState()); 333 334 assertTrue(TextUtils.equals(mEditText1.getText(), mEditText2.getText())); 335 } 336 337 @UiThreadTest 338 @Test testOnSaveInstanceState_savesTextStateWhenFreezesTextIfFalse()339 public void testOnSaveInstanceState_savesTextStateWhenFreezesTextIfFalse() { 340 // prepare EditText for before saveInstanceState 341 final String testStr = "This is a test str"; 342 mEditText1.setFreezesText(false); 343 mEditText1.setText(testStr); 344 345 // prepare EditText for after saveInstanceState 346 mEditText2.setFreezesText(false); 347 348 mEditText2.onRestoreInstanceState(mEditText1.onSaveInstanceState()); 349 350 assertTrue(TextUtils.equals(mEditText1.getText(), mEditText2.getText())); 351 } 352 353 @UiThreadTest 354 @Test testOnSaveInstanceState_savesSelectionStateWhenFreezesTextIsFalse()355 public void testOnSaveInstanceState_savesSelectionStateWhenFreezesTextIsFalse() { 356 // prepare EditText for before saveInstanceState 357 final String testStr = "This is a test str"; 358 mEditText1.setFreezesText(false); 359 mEditText1.setText(testStr); 360 mEditText1.setSelection(2, testStr.length() - 2); 361 362 // prepare EditText for after saveInstanceState 363 mEditText2.setFreezesText(false); 364 365 mEditText2.onRestoreInstanceState(mEditText1.onSaveInstanceState()); 366 367 assertEquals(mEditText1.getSelectionStart(), mEditText2.getSelectionStart()); 368 assertEquals(mEditText1.getSelectionEnd(), mEditText2.getSelectionEnd()); 369 } 370 371 @UiThreadTest 372 @Test testOnSaveInstanceState_savesSelectionStateWhenFreezesTextIsTrue()373 public void testOnSaveInstanceState_savesSelectionStateWhenFreezesTextIsTrue() { 374 // prepare EditText for before saveInstanceState 375 final String testStr = "This is a test str"; 376 mEditText1.setFreezesText(true); 377 mEditText1.setText(testStr); 378 mEditText1.setSelection(2, testStr.length() - 2); 379 380 // prepare EditText for after saveInstanceState 381 mEditText2.setFreezesText(true); 382 383 mEditText2.onRestoreInstanceState(mEditText1.onSaveInstanceState()); 384 385 assertEquals(mEditText1.getSelectionStart(), mEditText2.getSelectionStart()); 386 assertEquals(mEditText1.getSelectionEnd(), mEditText2.getSelectionEnd()); 387 } 388 isWatch()389 private boolean isWatch() { 390 return (mActivity.getResources().getConfiguration().uiMode 391 & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH; 392 } 393 394 @Test testHyphenationFrequencyDefaultValue()395 public void testHyphenationFrequencyDefaultValue() { 396 final Context context = InstrumentationRegistry.getTargetContext(); 397 final EditText editText = new EditText(context); 398 399 // Hypenation is enabled by default on watches to fit more text on their tiny screens. 400 if (isWatch()) { 401 assertEquals(Layout.HYPHENATION_FREQUENCY_NORMAL, editText.getHyphenationFrequency()); 402 } else { 403 assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, editText.getHyphenationFrequency()); 404 } 405 } 406 407 @Test testBreakStrategyDefaultValue()408 public void testBreakStrategyDefaultValue() { 409 final Context context = InstrumentationRegistry.getTargetContext(); 410 final EditText editText = new EditText(context); 411 assertEquals(Layout.BREAK_STRATEGY_SIMPLE, editText.getBreakStrategy()); 412 } 413 414 @UiThreadTest 415 @Test testOnInitializeA11yNodeInfo_hasAccessibilityActions()416 public void testOnInitializeA11yNodeInfo_hasAccessibilityActions() { 417 mEditText1.setText("android"); 418 final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 419 mEditText1.onInitializeAccessibilityNodeInfo(info); 420 List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList(); 421 assertTrue("info's isLongClickable should be true", 422 info.isLongClickable()); 423 assertTrue("info should have ACTION_LONG_CLICK", 424 actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)); 425 assertTrue("info should have ACTION_SET_TEXT", 426 actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT)); 427 428 } 429 430 private class MockEditText extends EditText { MockEditText(Context context)431 public MockEditText(Context context) { 432 super(context); 433 } 434 MockEditText(Context context, AttributeSet attrs)435 public MockEditText(Context context, AttributeSet attrs) { 436 super(context, attrs); 437 } 438 MockEditText(Context context, AttributeSet attrs, int defStyle)439 public MockEditText(Context context, AttributeSet attrs, int defStyle) { 440 super(context, attrs, defStyle); 441 } 442 443 @Override getDefaultEditable()444 protected boolean getDefaultEditable() { 445 return super.getDefaultEditable(); 446 } 447 448 @Override getDefaultMovementMethod()449 protected MovementMethod getDefaultMovementMethod() { 450 return super.getDefaultMovementMethod(); 451 } 452 } 453 454 @Test testGetTextNonEditable()455 public void testGetTextNonEditable() { 456 // This subclass calls getText before the object is fully constructed. This should not cause 457 // a null pointer exception. 458 GetTextEditText editText = new GetTextEditText(mActivity); 459 } 460 461 private class GetTextEditText extends EditText { 462 GetTextEditText(Context context)463 GetTextEditText(Context context) { 464 super(context); 465 } 466 467 @Override setText(CharSequence text, BufferType type)468 public void setText(CharSequence text, BufferType type) { 469 Editable currentText = getText(); 470 super.setText(text, type); 471 } 472 } 473 474 @Test testGetTextBeforeConstructor()475 public void testGetTextBeforeConstructor() { 476 // This subclass calls getText before the TextView constructor. This should not cause 477 // a null pointer exception. 478 GetTextEditText2 editText = new GetTextEditText2(mActivity); 479 } 480 481 private class GetTextEditText2 extends EditText { 482 GetTextEditText2(Context context)483 GetTextEditText2(Context context) { 484 super(context); 485 } 486 487 @Override setOverScrollMode(int overScrollMode)488 public void setOverScrollMode(int overScrollMode) { 489 // This method is called by the View constructor before the TextView/EditText 490 // constructors. 491 Editable text = getText(); 492 } 493 } 494 495 @Test testCursorDrag()496 public void testCursorDrag() throws Exception { 497 AtomicReference<SparseArray<Point>> dragStartEnd = new AtomicReference<>(); 498 String text = "Hello, how are you today?"; 499 mInstrumentation.runOnMainSync(() -> { 500 mEditText1.setText(text); 501 mEditText1.requestFocus(); 502 mEditText1.setSelection(text.length()); 503 dragStartEnd.set(getScreenCoords(mEditText1, text.indexOf("y?"), text.indexOf("el"))); 504 }); 505 assertCursorPosition(mEditText1, text.length()); 506 assertTrue(mEditText1.hasFocus()); 507 508 // Simulate a drag gesture. The cursor should end up at the position where the finger is 509 // lifted. 510 mCtsTouchUtils.emulateDragGesture(mInstrumentation, mActivityRule, dragStartEnd.get()); 511 assertCursorPosition(mEditText1, text.indexOf("el")); 512 } 513 assertCursorPosition(TextView textView, int expectedOffset)514 private static void assertCursorPosition(TextView textView, int expectedOffset) { 515 assertEquals(expectedOffset, textView.getSelectionStart()); 516 assertEquals(expectedOffset, textView.getSelectionEnd()); 517 } 518 getScreenCoords(TextView textView, int ... offsets)519 private static SparseArray<Point> getScreenCoords(TextView textView, int ... offsets) { 520 SparseArray<Point> result = new SparseArray<>(offsets.length); 521 for (int i = 0; i < offsets.length; i++) { 522 result.append(i, getScreenCoords(textView, offsets[i])); 523 } 524 return result; 525 } 526 getScreenCoords(TextView textView, int offset)527 private static Point getScreenCoords(TextView textView, int offset) { 528 // Get the x,y coordinates for the given offset in the text. 529 // These are relative to the view. 530 // Please note that we compensate for rounding error here by adding 1. 531 int x = (int) textView.getLayout().getPrimaryHorizontal(offset) + 1; 532 int line = textView.getLayout().getLineForOffset(offset); 533 int yTop = textView.getLayout().getLineTop(line); 534 int yBottom = textView.getLayout().getLineBottom(line); 535 int y = (yTop + yBottom) / 2; 536 537 // Get the x,y coordinates of the view. 538 final int[] viewOnScreenXY = new int[2]; 539 textView.getLocationOnScreen(viewOnScreenXY); 540 541 // Return the absolute screen coordinates for the given offset in the text. 542 return new Point(viewOnScreenXY[0] + x, viewOnScreenXY[1] + y); 543 } 544 545 @Test testEnterKey()546 public void testEnterKey() throws Throwable { 547 mActivityRule.runOnUiThread(() -> { 548 mEditText1.setSingleLine(true); 549 mEditText1.requestFocus(); 550 }); 551 552 mCtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mEditText1, KeyEvent.KEYCODE_ENTER); 553 mInstrumentation.waitForIdleSync(); 554 assertTrue(mEditText2.hasFocus()); 555 556 mActivityRule.runOnUiThread(() -> mEditText1.requestFocus()); 557 mCtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mEditText1, KeyEvent.KEYCODE_NUMPAD_ENTER); 558 assertTrue(mEditText2.hasFocus()); 559 } 560 561 private static final int FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT = 5000; 562 563 @UiThreadTest 564 @Test testSingleLineMaxLength_explicit_singleLine()565 public void testSingleLineMaxLength_explicit_singleLine() { 566 mActivity.setContentView(R.layout.edittext_singleline_maxlength); 567 568 EditText et = (EditText) mActivity.findViewById( 569 R.id.edittext_explicit_singleline_max_length); 570 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 571 } 572 573 @UiThreadTest 574 @Test testSingleLineMaxLength_explicit_singleLine_with_explicit_maxLength()575 public void testSingleLineMaxLength_explicit_singleLine_with_explicit_maxLength() { 576 mActivity.setContentView(R.layout.edittext_singleline_maxlength); 577 578 EditText et = (EditText) mActivity.findViewById( 579 R.id.edittext_explicit_singleline_with_explicit_max_length); 580 // This EditText has maxLength=2000 and singeLine=true. 581 // User specified maxLength must be respected. 582 assertTrue(et.getText().length() <= 2000); 583 } 584 585 @UiThreadTest 586 @Test testSingleLineMaxLength_singleLine_from_inputType()587 public void testSingleLineMaxLength_singleLine_from_inputType() { 588 mActivity.setContentView(R.layout.edittext_singleline_maxlength); 589 590 EditText et = (EditText) mActivity.findViewById(R.id.edittext_singleLine); 591 // This EditText has inputType="text" which is translated to singleLine. 592 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 593 } 594 595 @UiThreadTest 596 @Test testSingleLineMaxLength_multiline()597 public void testSingleLineMaxLength_multiline() { 598 mActivity.setContentView(R.layout.edittext_singleline_maxlength); 599 600 EditText et = (EditText) mActivity.findViewById(R.id.edittext_multiLine); 601 // Multiline text doesn't have automated char limit. 602 assertTrue(et.getText().length() > FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 603 } 604 605 @UiThreadTest 606 @Test testSingleLineMaxLength_textView()607 public void testSingleLineMaxLength_textView() { 608 mActivity.setContentView(R.layout.edittext_singleline_maxlength); 609 610 TextView tv = (TextView) mActivity.findViewById( 611 R.id.textview_explicit_singleline_max_length); 612 // Automated maxLength for singline text is not applied to TextView. 613 assertTrue(tv.getText().length() > FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 614 } 615 616 @UiThreadTest 617 @Test testSingleLineMaxLength_SetSingleLine()618 public void testSingleLineMaxLength_SetSingleLine() { 619 EditText et = new EditText(mActivity); 620 et.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 621 et.setSingleLine(); 622 623 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 624 } 625 626 @UiThreadTest 627 @Test testSingleLineMaxLength_setInputType_singleLine()628 public void testSingleLineMaxLength_setInputType_singleLine() { 629 EditText et = new EditText(mActivity); 630 et.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 631 et.setInputType(EditorInfo.TYPE_CLASS_TEXT); 632 633 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 634 } 635 636 @UiThreadTest 637 @Test testSingleLineMaxLength_setInputType_multiLine()638 public void testSingleLineMaxLength_setInputType_multiLine() { 639 EditText et = new EditText(mActivity); 640 et.setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); 641 et.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 642 643 assertTrue(et.getText().length() > FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 644 } 645 646 class DummyFilter implements InputFilter { 647 @Override filter( CharSequence source, int start, int end, Spanned dest, int dstart, int dend)648 public CharSequence filter( 649 CharSequence source, 650 int start, 651 int end, 652 Spanned dest, 653 int dstart, 654 int dend) { 655 return source; 656 } 657 } 658 659 private final InputFilter mFilterA = new DummyFilter(); 660 private final InputFilter mFilterB = new DummyFilter(); 661 662 @UiThreadTest 663 @Test testSingleLineMaxLength_SetSingleLine_preserveFilters()664 public void testSingleLineMaxLength_SetSingleLine_preserveFilters() { 665 EditText et = new EditText(mActivity); 666 et.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 667 et.setFilters(new InputFilter[] { mFilterA, mFilterB }); 668 et.setSingleLine(); 669 670 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 671 672 assertEquals(3, et.getFilters().length); 673 assertEquals(et.getFilters()[0], mFilterA); 674 assertEquals(et.getFilters()[1], mFilterB); 675 assertTrue(et.getFilters()[2] instanceof InputFilter.LengthFilter); 676 677 et.setSingleLine(false); 678 assertEquals(2, et.getFilters().length); 679 assertEquals(et.getFilters()[0], mFilterA); 680 assertEquals(et.getFilters()[1], mFilterB); 681 } 682 683 @UiThreadTest 684 @Test testSingleLineMaxLength_SetSingleLine_preserveFilters_mixtureFilters()685 public void testSingleLineMaxLength_SetSingleLine_preserveFilters_mixtureFilters() { 686 EditText et = new EditText(mActivity); 687 et.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 688 et.setSingleLine(); 689 et.setFilters(new InputFilter[] { mFilterA, et.getFilters()[0], mFilterB }); 690 691 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 692 693 et.setSingleLine(false); 694 assertEquals(2, et.getFilters().length); 695 assertEquals(et.getFilters()[0], mFilterA); 696 assertEquals(et.getFilters()[1], mFilterB); 697 } 698 699 @UiThreadTest 700 @Test testSingleLineMaxLength_SetSingleLine_preserveFilters_anotherLengthFilter()701 public void testSingleLineMaxLength_SetSingleLine_preserveFilters_anotherLengthFilter() { 702 EditText et = new EditText(mActivity); 703 et.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 704 final InputFilter myFilter = 705 new InputFilter.LengthFilter(FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 706 et.setFilters(new InputFilter[] { myFilter }); 707 et.setSingleLine(); 708 709 assertTrue(et.getText().length() <= FRAMEWORK_MAX_LENGTH_FOR_SINGLE_LINE_EDIT_TEXT); 710 711 // setSingleLine(true) must not add new filter since there is already LengthFilter. 712 assertEquals(1, et.getFilters().length); 713 assertEquals(et.getFilters()[0], myFilter); 714 715 // setSingleLine(false) must not remove my custom filter. 716 et.setSingleLine(false); 717 assertEquals(1, et.getFilters().length); 718 assertEquals(et.getFilters()[0], myFilter); 719 } 720 721 @UiThreadTest 722 @Test testInputTypeForConversionSuggestions()723 public void testInputTypeForConversionSuggestions() { 724 EditText editText = new EditText(mActivity); 725 editText.setInputType(EditorInfo.TYPE_CLASS_TEXT 726 | EditorInfo.TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS); 727 editText.setText(mActivity.getResources().getText(R.string.even_more_long_text)); 728 729 // The value of the input type is put into the EditorInfo parameter, and then the 730 // InputMethodManager can retrieve the value of the input type from EditorInfo. 731 EditorInfo editorInfo = new EditorInfo(); 732 editText.onCreateInputConnection(editorInfo); 733 734 assertEquals(InputType.TYPE_CLASS_TEXT 735 | InputType.TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS, 736 editorInfo.inputType); 737 } 738 739 @UiThreadTest 740 @Test testAttributeTextConversionSuggestion()741 public void testAttributeTextConversionSuggestion() { 742 mActivity.setContentView(R.layout.edittext_layout); 743 TextView tv = (TextView) mActivity.findViewById( 744 R.id.edittext_conversion_suggestion); 745 746 assertEquals(InputType.TYPE_CLASS_TEXT 747 | InputType.TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS, tv.getInputType()); 748 } 749 750 @Test testClickTwice_showIme()751 public void testClickTwice_showIme() throws Throwable { 752 try (MockImeSession imeSession = MockImeSession.create( 753 mInstrumentation.getContext(), 754 mInstrumentation.getUiAutomation(), 755 new ImeSettings.Builder())) { 756 757 clickOnEditText1(); 758 mInstrumentation.waitForIdleSync(); 759 760 clickOnEditText1(); 761 mInstrumentation.waitForIdleSync(); 762 763 final ImeEventStream stream = imeSession.openEventStream(); 764 expectEvent(stream, 765 event -> "showSoftInput".equals(event.getEventName()), 766 TimeUnit.SECONDS.toMillis(2)); 767 } 768 } 769 clickOnEditText1()770 private void clickOnEditText1() throws Exception { 771 final UiObject2 object = UiDevice.getInstance(mInstrumentation) 772 .findObject(By.res("android.widget.cts", "edittext_simple1")); 773 object.click(); 774 SystemClock.sleep(ViewConfiguration.getDoubleTapTimeout() + 50); 775 } 776 777 @Test testCursorNotBlinkingOnNewActivity_WithoutFocus()778 public void testCursorNotBlinkingOnNewActivity_WithoutFocus() { 779 Activity testActivity = mEmptyActivityRule.launchActivity(null); 780 EditText et = testActivity.findViewById(R.id.edittext_simple1); 781 Editor editor = et.getEditorForTesting(); 782 boolean cursorBlinking = editor.isBlinking(); 783 assertFalse(cursorBlinking); 784 } 785 786 @Test testCursorBlinkingOnNewActivity_WithFocus()787 public void testCursorBlinkingOnNewActivity_WithFocus() { 788 Activity testActivity = mEmptyActivityRule.launchActivity(null); 789 EditText et = testActivity.findViewById(R.id.edittext_simple1); 790 Editor editor = et.getEditorForTesting(); 791 792 mInstrumentation.runOnMainSync(() -> { 793 et.requestFocus(); 794 }); 795 796 boolean cursorBlinking = editor.isBlinking(); 797 assertTrue(cursorBlinking); 798 } 799 800 @Test testSuspendAndResumeBlinkingCursor()801 public void testSuspendAndResumeBlinkingCursor() { 802 Activity testActivity = mEmptyActivityRule.launchActivity(null); 803 final EditText et = testActivity.findViewById(R.id.edittext_simple1); 804 Editor editor = et.getEditorForTesting(); 805 806 mInstrumentation.runOnMainSync(() -> { 807 et.requestFocus(); 808 }); 809 810 UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 811 812 boolean cursorBlinking = editor.isBlinking(); 813 assertTrue(cursorBlinking); 814 815 // Send activity to the background. 816 device.pressHome(); 817 device.waitForIdle(); 818 819 cursorBlinking = editor.isBlinking(); 820 assertFalse(cursorBlinking); 821 822 // Bring the activity back into the foreground 823 Intent resumeActivity = new Intent(mInstrumentation.getContext(), 824 EditTextCursorCtsActivity.class); 825 resumeActivity.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 826 mActivity.startActivity(resumeActivity); 827 828 // Check if the activity is in the foreground. 829 device.wait(Until.findObject(By.text("test for blinking cursor")), 2000); 830 831 mInstrumentation.runOnMainSync(() -> { 832 et.requestFocus(); 833 }); 834 835 cursorBlinking = editor.isBlinking(); 836 assertTrue(cursorBlinking); 837 } 838 839 /* 840 * This test makes sure the cursor is blinking when an EditText view is dynamically added to 841 * the layout when it has focus. 842 */ 843 @Test testCursorBlinking_ViewDynamicallyAdded_WithFocus()844 public void testCursorBlinking_ViewDynamicallyAdded_WithFocus() { 845 Activity testActivity = mEmptyActivityRule.launchActivity(null); 846 EditText editText = new EditText(testActivity.getApplicationContext()); 847 Editor editor = editText.getEditorForTesting(); 848 LinearLayout layout = testActivity.findViewById(R.id.edit_text); 849 850 editText.setLayoutParams(layout.getLayoutParams()); 851 852 mInstrumentation.runOnMainSync(() -> { 853 editText.requestFocus(); 854 layout.addView(editText); 855 }); 856 857 assertTrue(editor.isBlinking()); 858 } 859 860 /* 861 * This test makes sure the cursor is not blinking when an EditText view is dynamically added 862 * to the layout. 863 */ 864 @Test testCursorNotBlinking_ViewDynamicallyAdded_NoFocus()865 public void testCursorNotBlinking_ViewDynamicallyAdded_NoFocus() { 866 Activity testActivity = mEmptyActivityRule.launchActivity(null); 867 EditText editText = new EditText(testActivity.getApplicationContext()); 868 Editor editor = editText.getEditorForTesting(); 869 LinearLayout layout = testActivity.findViewById(R.id.edit_text); 870 871 editText.setLayoutParams(layout.getLayoutParams()); 872 873 mInstrumentation.runOnMainSync(() -> { 874 layout.addView(editText); 875 }); 876 877 assertFalse(editor.isBlinking()); 878 } 879 880 /* 881 * This test makes sure blink is suspended when the EditText view is dynamically removed from 882 * the layout. 883 */ 884 @Test testCursorSuspendBlinking_ViewDynamicallyRemoved()885 public void testCursorSuspendBlinking_ViewDynamicallyRemoved() { 886 Activity testActivity = mEmptyActivityRule.launchActivity(null); 887 final EditText editText = testActivity.findViewById(R.id.edittext_simple1); 888 Editor editor = editText.getEditorForTesting(); 889 890 mInstrumentation.runOnMainSync(() -> { 891 editText.requestFocus(); 892 }); 893 894 assertTrue(editor.isBlinking()); 895 896 mInstrumentation.runOnMainSync(() -> { 897 ViewGroup viewGroup = (ViewGroup) editText.getParent(); 898 viewGroup.removeView(editText); 899 }); 900 901 assertFalse(editor.isBlinking()); 902 } 903 904 /* 905 * This test checks that an EditText view that never had focus can be removed and added 906 * dynamically to the layout and when focus is requested the cursor blinks. 907 */ 908 @Test testCursorBlinking_ViewDynamically_RemovedAdded_NeverHadFocus()909 public void testCursorBlinking_ViewDynamically_RemovedAdded_NeverHadFocus() { 910 Activity testActivity = mEmptyActivityRule.launchActivity(null); 911 final EditText editText = testActivity.findViewById(R.id.edittext_simple1); 912 Editor editor = editText.getEditorForTesting(); 913 914 mInstrumentation.runOnMainSync(() -> { 915 ViewGroup viewGroup = (ViewGroup) editText.getParent(); 916 viewGroup.removeView(editText); 917 }); 918 919 LinearLayout layout = testActivity.findViewById(R.id.edit_text); 920 layout.addView(editText); 921 922 mInstrumentation.runOnMainSync(() -> { 923 editText.requestFocus(); 924 }); 925 926 assertTrue(editor.isBlinking()); 927 } 928 929 /* 930 * This test checks that a focused EditText view can be removed and added back to the layout 931 * dynamically and the cursor resumes blinking. 932 */ 933 @Test testCursorResumeBlinking_AfterFocusedView_DynamicallyRemovedAdded()934 public void testCursorResumeBlinking_AfterFocusedView_DynamicallyRemovedAdded() { 935 Activity testActivity = mEmptyActivityRule.launchActivity(null); 936 final EditText editText = testActivity.findViewById(R.id.edittext_simple1); 937 LinearLayout layout = testActivity.findViewById(R.id.edit_text); 938 Editor editor = editText.getEditorForTesting(); 939 940 mInstrumentation.runOnMainSync(() -> { 941 editText.requestFocus(); 942 }); 943 944 assertTrue(editor.isBlinking()); 945 946 mInstrumentation.runOnMainSync(() -> { 947 ViewGroup viewGroup = (ViewGroup) editText.getParent(); 948 viewGroup.removeView(editText); 949 }); 950 951 assertFalse(editor.isBlinking()); 952 953 mInstrumentation.runOnMainSync(() -> { 954 layout.addView(editText); 955 }); 956 957 mInstrumentation.runOnMainSync(() -> { 958 editText.requestFocus(); 959 editText.setText("This has been re-added"); 960 }); 961 962 assertTrue(editor.isBlinking()); 963 964 } 965 966 } 967