• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.view.inputmethod.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 
27 import android.os.Bundle;
28 import android.os.LocaleList;
29 import android.os.Parcel;
30 import android.test.MoreAsserts;
31 import android.text.SpannableStringBuilder;
32 import android.text.TextUtils;
33 import android.util.StringBuilderPrinter;
34 import android.view.MotionEvent;
35 import android.view.inputmethod.DeleteGesture;
36 import android.view.inputmethod.EditorInfo;
37 import android.view.inputmethod.HandwritingGesture;
38 import android.view.inputmethod.InputConnection;
39 import android.view.inputmethod.InsertGesture;
40 import android.view.inputmethod.PreviewableHandwritingGesture;
41 import android.view.inputmethod.SelectGesture;
42 import android.view.inputmethod.SurroundingText;
43 
44 import androidx.test.filters.SmallTest;
45 import androidx.test.runner.AndroidJUnit4;
46 
47 import com.android.compatibility.common.util.ApiTest;
48 
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.HashSet;
55 import java.util.List;
56 import java.util.Set;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59 
60 @SmallTest
61 @RunWith(AndroidJUnit4.class)
62 public class EditorInfoTest {
63     private static final int TEST_TEXT_LENGTH = 2048;
64     /** A text with 1 million chars! This is way too long. */
65     private static final int OVER_SIZED_TEXT_LENGTH = 1 * 1024 * 1024;
66     /** To get the longest available text from getInitialText methods. */
67     private static final int REQUEST_LONGEST_AVAILABLE_TEXT = OVER_SIZED_TEXT_LENGTH; //
68 
69     @Test
70     @ApiTest(apis = {"android.view.inputmethod.EditorInfo#setSupportedHandwritingGestures",
71             "android.view.inputmethod.EditorInfo#setInitialToolType",
72             "android.view.inputmethod.EditorInfo#getSupportedHandwritingGestures",
73             "android.view.inputmethod.EditorInfo#getInitialToolType"})
testEditorInfo()74     public void testEditorInfo() {
75         EditorInfo info = new EditorInfo();
76         CharSequence testInitialText = createTestText(TEST_TEXT_LENGTH);
77 
78         info.actionId = 1;
79         info.actionLabel = "actionLabel";
80         info.fieldId = 2;
81         info.fieldName = "fieldName";
82         info.hintText = "hintText";
83         info.imeOptions = EditorInfo.IME_FLAG_NO_ENTER_ACTION;
84         info.initialCapsMode = TextUtils.CAP_MODE_CHARACTERS;
85         info.initialSelEnd = 10;
86         info.initialSelStart = 0;
87         info.inputType = EditorInfo.TYPE_MASK_CLASS;
88         info.label = "label";
89         info.packageName = "android.view.cts";
90         info.privateImeOptions = "privateIme";
91         Bundle b = new Bundle();
92         info.setInitialSurroundingText(testInitialText);
93         String key = "bundleKey";
94         String value = "bundleValue";
95         b.putString(key, value);
96         info.extras = b;
97         info.hintLocales = LocaleList.forLanguageTags("en-PH,en-US");
98         info.contentMimeTypes = new String[]{"image/gif", "image/png"};
99         info.setInitialToolType(MotionEvent.TOOL_TYPE_FINGER);
100         info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
101         info.setSupportedHandwritingGesturePreviews(
102                 Stream.of(SelectGesture.class).collect(Collectors.toSet()));
103 
104         assertEquals(0, info.describeContents());
105 
106         Parcel p = Parcel.obtain();
107         info.writeToParcel(p, 0);
108         p.setDataPosition(0);
109         EditorInfo targetInfo = EditorInfo.CREATOR.createFromParcel(p);
110         p.recycle();
111         assertEquals(info.actionId, targetInfo.actionId);
112         assertEquals(info.fieldId, targetInfo.fieldId);
113         assertEquals(info.fieldName, targetInfo.fieldName);
114         assertEquals(info.imeOptions, targetInfo.imeOptions);
115         assertEquals(info.initialCapsMode, targetInfo.initialCapsMode);
116         assertEquals(info.initialSelEnd, targetInfo.initialSelEnd);
117         assertEquals(info.initialSelStart, targetInfo.initialSelStart);
118         assertEquals(info.inputType, targetInfo.inputType);
119         assertEquals(info.packageName, targetInfo.packageName);
120         assertEquals(info.privateImeOptions, targetInfo.privateImeOptions);
121         assertTrue(TextUtils.equals(testInitialText, concatInitialSurroundingText(targetInfo)));
122         assertEquals(info.hintText.toString(), targetInfo.hintText.toString());
123         assertEquals(info.actionLabel.toString(), targetInfo.actionLabel.toString());
124         assertEquals(info.label.toString(), targetInfo.label.toString());
125         assertEquals(info.extras.getString(key), targetInfo.extras.getString(key));
126         assertEquals(info.hintLocales, targetInfo.hintLocales);
127         assertEquals(info.getInitialToolType(), targetInfo.getInitialToolType());
128         assertEquals(info.getSupportedHandwritingGestures(),
129                 targetInfo.getSupportedHandwritingGestures());
130         MoreAsserts.assertEquals(info.contentMimeTypes, targetInfo.contentMimeTypes);
131 
132         StringBuilder sb = new StringBuilder();
133         StringBuilderPrinter sbPrinter = new StringBuilderPrinter(sb);
134         String prefix = "TestEditorInfo";
135         info.dump(sbPrinter, prefix);
136 
137         assertFalse(TextUtils.isEmpty(sb.toString()));
138         assertFalse(sb.toString().contains(testInitialText));
139     }
140 
141     @ApiTest(apis = {"android.view.inputmethod.EditorInfo#setSupportedHandwritingGestures",
142             "android.view.inputmethod.EditorInfo#getSupportedHandwritingGestures"})
143     @Test
testSupportedHandwritingGestures()144     public void testSupportedHandwritingGestures() {
145         EditorInfo info = new EditorInfo();
146         info.setSupportedHandwritingGestures(new ArrayList<>());
147         assertTrue(info.getSupportedHandwritingGestures().isEmpty());
148 
149         info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
150         assertEquals(info.getSupportedHandwritingGestures().get(0), SelectGesture.class);
151 
152         info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class, InsertGesture.class,
153                 DeleteGesture.class));
154         List<Class<? extends HandwritingGesture>> gestures = info.getSupportedHandwritingGestures();
155         assertEquals(gestures.size(), 3);
156         assertTrue(gestures.contains(SelectGesture.class));
157         assertTrue(gestures.contains(DeleteGesture.class));
158         assertTrue(gestures.contains(InsertGesture.class));
159     }
160 
161     @ApiTest(apis = {"android.view.inputmethod.EditorInfo#setSupportedHandwritingGesturePreviews",
162             "android.view.inputmethod.EditorInfo#getSupportedHandwritingGesturePreviews",
163             "android.view.inputmethod.EditorInfo#getSupportedHandwritingGestures"})
164     @Test
testSupportedHandwritingGesturePreviews()165     public void testSupportedHandwritingGesturePreviews() {
166         EditorInfo info = new EditorInfo();
167         info.setSupportedHandwritingGesturePreviews(new HashSet<>());
168         assertTrue(info.getSupportedHandwritingGesturePreviews().isEmpty());
169 
170         Set<Class<? extends PreviewableHandwritingGesture>> selectGestureSet =
171                 Stream.of(SelectGesture.class).collect(Collectors.toSet());
172         info.setSupportedHandwritingGesturePreviews(selectGestureSet);
173         assertEquals(info.getSupportedHandwritingGesturePreviews(), selectGestureSet);
174 
175         assertNotEquals(info.getSupportedHandwritingGesturePreviews(),
176                 info.getSupportedHandwritingGestures());
177 
178         info.setSupportedHandwritingGesturePreviews(
179                 Stream.of(SelectGesture.class, DeleteGesture.class).collect(Collectors.toSet()));
180         Set<Class<? extends PreviewableHandwritingGesture>> gestures =
181                 info.getSupportedHandwritingGesturePreviews();
182         assertEquals(gestures.size(), 2);
183         assertTrue(gestures.contains(SelectGesture.class));
184         assertTrue(gestures.contains(DeleteGesture.class));
185     }
186 
187     @Test
testNullHintLocals()188     public void testNullHintLocals() {
189         EditorInfo info = new EditorInfo();
190         info.hintLocales = null;
191         Parcel p = Parcel.obtain();
192         info.writeToParcel(p, 0);
193         p.setDataPosition(0);
194         EditorInfo targetInfo = EditorInfo.CREATOR.createFromParcel(p);
195         p.recycle();
196         assertNull(targetInfo.hintLocales);
197     }
198 
199     @Test
testInitialSurroundingText_nullInput_throwsException()200     public void testInitialSurroundingText_nullInput_throwsException() {
201         final EditorInfo info = new EditorInfo();
202 
203         try {
204             info.setInitialSurroundingText(null);
205             fail("Shall not take null input");
206         } catch (NullPointerException expected) {
207             // Expected behavior, nothing to do.
208         }
209     }
210 
211     @Test
testInitialSurroundingText_passwordTypes_notObtain()212     public void testInitialSurroundingText_passwordTypes_notObtain() {
213         final EditorInfo info = new EditorInfo();
214         final CharSequence testInitialText = createTestText(/* size= */ 10);
215         info.initialSelStart = 1;
216         info.initialSelEnd = 2;
217 
218         // Text password type
219         info.inputType = (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
220 
221         info.setInitialSurroundingText(testInitialText);
222 
223         assertExpectedTextLength(info,
224                 /* expectBeforeCursorLength= */null,
225                 /* expectSelectionLength= */null,
226                 /* expectAfterCursorLength= */null,
227                 /* expectSurroundingText= */null);
228 
229         // Web password type
230         info.inputType = (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD);
231 
232         info.setInitialSurroundingText(testInitialText);
233 
234         assertExpectedTextLength(info,
235                 /* expectBeforeCursorLength= */null,
236                 /* expectSelectionLength= */null,
237                 /* expectAfterCursorLength= */null,
238                 /* expectSurroundingText= */null);
239 
240         // Number password type
241         info.inputType = (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
242 
243         info.setInitialSurroundingText(testInitialText);
244 
245         assertExpectedTextLength(info,
246                 /* expectBeforeCursorLength= */null,
247                 /* expectSelectionLength= */null,
248                 /* expectAfterCursorLength= */null,
249                 /* expectSurroundingText= */null);
250     }
251 
252     @Test
testInitialSurroundingText_cursorAtHead_emptyBeforeCursorText()253     public void testInitialSurroundingText_cursorAtHead_emptyBeforeCursorText() {
254         final EditorInfo info = new EditorInfo();
255         final CharSequence testText = createTestText(TEST_TEXT_LENGTH);
256         final int selLength = 10;
257         info.initialSelStart = 0;
258         info.initialSelEnd = info.initialSelStart + selLength;
259         final int expectedTextBeforeCursorLength = 0;
260         final int expectedTextAfterCursorLength = testText.length() - selLength;
261         final SurroundingText expectedSurroundingText =
262                 new SurroundingText(testText, info.initialSelStart, info.initialSelEnd, 0);
263 
264         info.setInitialSurroundingText(testText);
265 
266         assertExpectedTextLength(info, expectedTextBeforeCursorLength, selLength,
267                 expectedTextAfterCursorLength, expectedSurroundingText);
268     }
269 
270     @Test
testInitialSurroundingText_cursorAtTail_emptyAfterCursorText()271     public void testInitialSurroundingText_cursorAtTail_emptyAfterCursorText() {
272         final EditorInfo info = new EditorInfo();
273         final CharSequence testText = createTestText(TEST_TEXT_LENGTH);
274         final int selLength = 10;
275         info.initialSelStart = testText.length() - selLength;
276         info.initialSelEnd = testText.length();
277         final int expectedTextBeforeCursorLength = testText.length() - selLength;
278         final int expectedTextAfterCursorLength = 0;
279         final SurroundingText expectedSurroundingText =
280                 new SurroundingText(testText, info.initialSelStart, info.initialSelEnd, 0);
281 
282         info.setInitialSurroundingText(testText);
283 
284         assertExpectedTextLength(info, expectedTextBeforeCursorLength, selLength,
285                 expectedTextAfterCursorLength, expectedSurroundingText);
286     }
287 
288     @Test
testInitialSurroundingText_noSelection_emptySelectionText()289     public void testInitialSurroundingText_noSelection_emptySelectionText() {
290         final EditorInfo info = new EditorInfo();
291         final CharSequence testText = createTestText(TEST_TEXT_LENGTH);
292         final int selLength = 0;
293         info.initialSelStart = 0;
294         info.initialSelEnd = info.initialSelStart + selLength;
295         final int expectedTextBeforeCursorLength = 0;
296         final int expectedTextAfterCursorLength = testText.length();
297         final SurroundingText expectedSurroundingText =
298                 new SurroundingText(testText, info.initialSelStart, info.initialSelEnd, 0);
299 
300         info.setInitialSurroundingText(testText);
301 
302         assertExpectedTextLength(info, expectedTextBeforeCursorLength, selLength,
303                 expectedTextAfterCursorLength, expectedSurroundingText);
304     }
305 
306     @Test
testInitialSurroundingText_overSizedSelection_keepsBeforeAfterTextValid()307     public void testInitialSurroundingText_overSizedSelection_keepsBeforeAfterTextValid() {
308         final EditorInfo info = new EditorInfo();
309         final CharSequence testText = createTestText(OVER_SIZED_TEXT_LENGTH);
310         final int selLength = OVER_SIZED_TEXT_LENGTH - 2;
311         info.initialSelStart = 1;
312         info.initialSelEnd = info.initialSelStart + selLength;
313         final int expectedTextBeforeCursorLength = 1;
314         final int expectedTextAfterCursorLength = 1;
315         final int offset = info.initialSelStart - expectedTextBeforeCursorLength;
316         final CharSequence beforeCursor = testText.subSequence(offset,
317                 offset + expectedTextBeforeCursorLength);
318         final CharSequence afterCursor = testText.subSequence(info.initialSelEnd,
319                 testText.length());
320         final CharSequence surroundingText = TextUtils.concat(beforeCursor, afterCursor);
321         final SurroundingText expectedSurroundingText =
322                 new SurroundingText(surroundingText, info.initialSelStart, info.initialSelStart, 0);
323 
324         info.setInitialSurroundingText(testText);
325 
326         assertExpectedTextLength(info, expectedTextBeforeCursorLength,
327                 /* expectSelectionLength= */null, expectedTextAfterCursorLength,
328                 expectedSurroundingText);
329 
330     }
331 
332     @Test
testInitialSurroundingSubText_keepsOriginalCursorPosition()333     public void testInitialSurroundingSubText_keepsOriginalCursorPosition() {
334         final EditorInfo info = new EditorInfo();
335         final String prefixString = "prefix";
336         final CharSequence subText = createTestText(TEST_TEXT_LENGTH);
337         final CharSequence originalText = TextUtils.concat(prefixString, subText);
338         final int selLength = 2;
339         info.initialSelStart = originalText.length() / 2;
340         info.initialSelEnd = info.initialSelStart + selLength;
341         final CharSequence expectedTextBeforeCursor = createExpectedText(/* startNumber= */0,
342                 info.initialSelStart - prefixString.length());
343         final CharSequence expectedSelectedText = createExpectedText(
344                 info.initialSelStart - prefixString.length(), selLength);
345         final CharSequence expectedTextAfterCursor = createExpectedText(
346                 info.initialSelEnd - prefixString.length(),
347                 originalText.length() - info.initialSelEnd);
348         final SurroundingText expectedSurroundingText = new SurroundingText(
349                 TextUtils.concat(expectedTextBeforeCursor, expectedSelectedText,
350                         expectedTextAfterCursor),
351                 info.initialSelStart - prefixString.length(),
352                 info.initialSelStart - prefixString.length() + selLength,
353                 prefixString.length());
354 
355         info.setInitialSurroundingSubText(subText, prefixString.length());
356 
357         assertTrue(TextUtils.equals(expectedTextBeforeCursor,
358                 info.getInitialTextBeforeCursor(REQUEST_LONGEST_AVAILABLE_TEXT,
359                         InputConnection.GET_TEXT_WITH_STYLES)));
360         assertTrue(TextUtils.equals(expectedSelectedText,
361                 info.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES)));
362         assertTrue(TextUtils.equals(expectedTextAfterCursor,
363                 info.getInitialTextAfterCursor(REQUEST_LONGEST_AVAILABLE_TEXT,
364                         InputConnection.GET_TEXT_WITH_STYLES)));
365         SurroundingText surroundingText = info.getInitialSurroundingText(
366                 REQUEST_LONGEST_AVAILABLE_TEXT,
367                 REQUEST_LONGEST_AVAILABLE_TEXT,
368                 InputConnection.GET_TEXT_WITH_STYLES);
369         assertNotNull(surroundingText);
370         assertTrue(TextUtils.equals(expectedSurroundingText.getText(), surroundingText.getText()));
371         assertEquals(expectedSurroundingText.getSelectionStart(),
372                 surroundingText.getSelectionStart());
373         assertEquals(expectedSurroundingText.getSelectionEnd(), surroundingText.getSelectionEnd());
374     }
375 
assertExpectedTextLength(EditorInfo editorInfo, Integer expectBeforeCursorLength, Integer expectSelectionLength, Integer expectAfterCursorLength, SurroundingText expectSurroundingText)376     private static void assertExpectedTextLength(EditorInfo editorInfo,
377             Integer expectBeforeCursorLength, Integer expectSelectionLength,
378             Integer expectAfterCursorLength,
379             SurroundingText expectSurroundingText) {
380         final CharSequence textBeforeCursor =
381                 editorInfo.getInitialTextBeforeCursor(REQUEST_LONGEST_AVAILABLE_TEXT,
382                         InputConnection.GET_TEXT_WITH_STYLES);
383         final CharSequence selectedText =
384                 editorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
385         final CharSequence textAfterCursor =
386                 editorInfo.getInitialTextAfterCursor(REQUEST_LONGEST_AVAILABLE_TEXT,
387                         InputConnection.GET_TEXT_WITH_STYLES);
388         final SurroundingText surroundingText = editorInfo.getInitialSurroundingText(
389                 REQUEST_LONGEST_AVAILABLE_TEXT,
390                 REQUEST_LONGEST_AVAILABLE_TEXT,
391                 InputConnection.GET_TEXT_WITH_STYLES);
392 
393         if (expectBeforeCursorLength == null) {
394             assertNull(textBeforeCursor);
395         } else {
396             assertEquals(expectBeforeCursorLength.intValue(), textBeforeCursor.length());
397         }
398 
399         if (expectSelectionLength == null) {
400             assertNull(selectedText);
401         } else {
402             assertEquals(expectSelectionLength.intValue(), selectedText.length());
403         }
404 
405         if (expectAfterCursorLength == null) {
406             assertNull(textAfterCursor);
407         } else {
408             assertEquals(expectAfterCursorLength.intValue(), textAfterCursor.length());
409         }
410 
411         if (expectSurroundingText == null) {
412             assertNull(surroundingText);
413         } else {
414             assertTrue(TextUtils.equals(
415                     expectSurroundingText.getText(), surroundingText.getText()));
416             assertEquals(expectSurroundingText.getSelectionStart(),
417                     surroundingText.getSelectionStart());
418             assertEquals(expectSurroundingText.getSelectionEnd(),
419                     surroundingText.getSelectionEnd());
420             assertEquals(expectSurroundingText.getOffset(), surroundingText.getOffset());
421         }
422     }
423 
createTestText(int size)424     private static CharSequence createTestText(int size) {
425         final SpannableStringBuilder builder = new SpannableStringBuilder();
426         for (int i = 0; i < size; i++) {
427             builder.append(Integer.toString(i % 10));
428         }
429         return builder;
430     }
431 
createExpectedText(int startNumber, int length)432     private static CharSequence createExpectedText(int startNumber, int length) {
433         final SpannableStringBuilder builder = new SpannableStringBuilder();
434         for (int i = startNumber; i < startNumber + length; i++) {
435             builder.append(Integer.toString(i % 10));
436         }
437         return builder;
438     }
439 
concatInitialSurroundingText(EditorInfo info)440     private static CharSequence concatInitialSurroundingText(EditorInfo info) {
441         final CharSequence textBeforeCursor =
442                 nullToEmpty(info.getInitialTextBeforeCursor(REQUEST_LONGEST_AVAILABLE_TEXT,
443                         InputConnection.GET_TEXT_WITH_STYLES));
444         final CharSequence selectedText =
445                 nullToEmpty(info.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES));
446         final CharSequence textAfterCursor =
447                 nullToEmpty(info.getInitialTextAfterCursor(REQUEST_LONGEST_AVAILABLE_TEXT,
448                         InputConnection.GET_TEXT_WITH_STYLES));
449 
450         return TextUtils.concat(textBeforeCursor, selectedText, textAfterCursor);
451     }
452 
nullToEmpty(CharSequence source)453     private static CharSequence nullToEmpty(CharSequence source) {
454         return (source == null) ? new SpannableStringBuilder("") : source;
455     }
456 }
457