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