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