• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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