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.view.inputmethod.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 25 import android.content.ClipDescription; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.text.Editable; 29 import android.text.Selection; 30 import android.text.Spannable; 31 import android.text.SpannableString; 32 import android.text.Spanned; 33 import android.text.TextUtils; 34 import android.view.View; 35 import android.view.inputmethod.BaseInputConnection; 36 import android.view.inputmethod.CompletionInfo; 37 import android.view.inputmethod.ExtractedTextRequest; 38 import android.view.inputmethod.InputContentInfo; 39 import android.view.inputmethod.InputMethodManager; 40 import android.view.inputmethod.cts.util.InputConnectionTestUtils; 41 42 import androidx.test.InstrumentationRegistry; 43 import androidx.test.filters.MediumTest; 44 import androidx.test.runner.AndroidJUnit4; 45 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 @MediumTest 50 @RunWith(AndroidJUnit4.class) 51 public class BaseInputConnectionTest { 52 53 private static BaseInputConnection createBaseInputConnection() { 54 final View view = new View(InstrumentationRegistry.getTargetContext()); 55 return new BaseInputConnection(view, true); 56 } 57 58 @Test 59 public void testDefaultMethods() { 60 // These methods are default to return fixed result. 61 final BaseInputConnection connection = createBaseInputConnection(); 62 63 assertFalse(connection.beginBatchEdit()); 64 assertFalse(connection.endBatchEdit()); 65 66 // only fit for test default implementation of commitCompletion. 67 int completionId = 1; 68 String completionString = "commitCompletion test"; 69 assertFalse(connection.commitCompletion(new CompletionInfo(completionId, 70 0, completionString))); 71 72 assertNull(connection.getExtractedText(new ExtractedTextRequest(), 0)); 73 74 // only fit for test default implementation of performEditorAction. 75 int actionCode = 1; 76 int actionId = 2; 77 String action = "android.intent.action.MAIN"; 78 assertTrue(connection.performEditorAction(actionCode)); 79 assertFalse(connection.performContextMenuAction(actionId)); 80 assertFalse(connection.performPrivateCommand(action, new Bundle())); 81 } 82 83 @Test 84 public void testOpComposingSpans() { 85 Spannable text = new SpannableString("Test ComposingSpans"); 86 BaseInputConnection.setComposingSpans(text); 87 assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1); 88 assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1); 89 BaseInputConnection.removeComposingSpans(text); 90 assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1); 91 assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1); 92 } 93 94 /** 95 * getEditable: Return the target of edit operations. The default implementation 96 * returns its own fake editable that is just used for composing text. 97 * clearMetaKeyStates: Default implementation uses 98 * MetaKeyKeyListener#clearMetaKeyState(long, int) to clear the state. 99 * BugId:1738511 100 * commitText: Default implementation replaces any existing composing text with the given 101 * text. 102 * deleteSurroundingText: The default implementation performs the deletion around the current 103 * selection position of the editable text. 104 * getCursorCapsMode: The default implementation uses TextUtils.getCapsMode to get the 105 * cursor caps mode for the current selection position in the editable text. 106 * TextUtils.getCapsMode is tested fully in TextUtilsTest#testGetCapsMode. 107 * getTextBeforeCursor, getTextAfterCursor: The default implementation performs the deletion 108 * around the current selection position of the editable text. 109 * setSelection: changes the selection position in the current editable text. 110 */ 111 @Test 112 public void testOpTextMethods() { 113 final BaseInputConnection connection = createBaseInputConnection(); 114 115 // return is an default Editable instance with empty source 116 final Editable text = connection.getEditable(); 117 assertNotNull(text); 118 assertEquals(0, text.length()); 119 120 // Test commitText, not dummy mode 121 CharSequence str = "TestCommit "; 122 Editable inputText = Editable.Factory.getInstance().newEditable(str); 123 connection.commitText(inputText, inputText.length()); 124 final Editable text2 = connection.getEditable(); 125 int strLength = str.length(); 126 assertEquals(strLength, text2.length()); 127 assertEquals(str.toString(), text2.toString()); 128 assertEquals(TextUtils.CAP_MODE_WORDS, 129 connection.getCursorCapsMode(TextUtils.CAP_MODE_WORDS)); 130 int offLength = 3; 131 CharSequence expected = str.subSequence(strLength - offLength, strLength); 132 assertEquals(expected.toString(), connection.getTextBeforeCursor(offLength, 133 BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 134 connection.setSelection(0, 0); 135 expected = str.subSequence(0, offLength); 136 assertEquals(expected.toString(), connection.getTextAfterCursor(offLength, 137 BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 138 139 // Test deleteSurroundingText 140 int end = text2.length(); 141 connection.setSelection(end, end); 142 // Delete the ending space 143 assertTrue(connection.deleteSurroundingText(1, 2)); 144 Editable text3 = connection.getEditable(); 145 assertEquals(strLength - 1, text3.length()); 146 String expectedDelString = "TestCommit"; 147 assertEquals(expectedDelString, text3.toString()); 148 } 149 150 /** 151 * finishComposingText: The default implementation removes the composing state from the 152 * current editable text. 153 * setComposingText: The default implementation places the given text into the editable, 154 * replacing any existing composing text 155 */ 156 @Test 157 public void testFinishComposingText() { 158 final BaseInputConnection connection = createBaseInputConnection(); 159 CharSequence str = "TestFinish"; 160 Editable inputText = Editable.Factory.getInstance().newEditable(str); 161 connection.commitText(inputText, inputText.length()); 162 final Editable text = connection.getEditable(); 163 // Test finishComposingText, not dummy mode 164 BaseInputConnection.setComposingSpans(text); 165 assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1); 166 assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1); 167 connection.finishComposingText(); 168 assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1); 169 assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1); 170 } 171 172 /** 173 * Updates InputMethodManager with the current fullscreen mode. 174 */ 175 @Test 176 public void testReportFullscreenMode() { 177 final InputMethodManager imm = InstrumentationRegistry.getInstrumentation() 178 .getTargetContext().getSystemService(InputMethodManager.class); 179 final BaseInputConnection connection = createBaseInputConnection(); 180 connection.reportFullscreenMode(false); 181 assertFalse(imm.isFullscreenMode()); 182 connection.reportFullscreenMode(true); 183 // Only IMEs are allowed to report full-screen mode. Calling this method from the 184 // application should have no effect. 185 assertFalse(imm.isFullscreenMode()); 186 } 187 188 /** 189 * An utility method to create an instance of {@link BaseInputConnection} in the full editor 190 * mode with an initial text and selection range. 191 * 192 * @param source the initial text. 193 * @return {@link BaseInputConnection} instantiated in the full editor mode with {@code source} 194 * and selection range from {@code selectionStart} to {@code selectionEnd} 195 */ 196 private static BaseInputConnection createConnectionWithSelection(CharSequence source) { 197 final int selectionStart = Selection.getSelectionStart(source); 198 final int selectionEnd = Selection.getSelectionEnd(source); 199 final Editable editable = Editable.Factory.getInstance().newEditable(source); 200 Selection.setSelection(editable, selectionStart, selectionEnd); 201 final View view = new View(InstrumentationRegistry.getTargetContext()); 202 return new BaseInputConnection(view, true) { 203 @Override 204 public Editable getEditable() { 205 return editable; 206 } 207 }; 208 } 209 210 private static void verifyDeleteSurroundingTextMain(final String initialState, 211 final int deleteBefore, final int deleteAfter, final String expectedState) { 212 final CharSequence source = InputConnectionTestUtils.formatString(initialState); 213 final BaseInputConnection ic = createConnectionWithSelection(source); 214 ic.deleteSurroundingText(deleteBefore, deleteAfter); 215 216 final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState); 217 final int expectedSelectionStart = Selection.getSelectionStart(expectedString); 218 final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString); 219 220 // It is sufficient to check the surrounding text up to source.length() characters, because 221 // InputConnection.deleteSurroundingText() is not supposed to increase the text length. 222 final int retrievalLength = source.length(); 223 if (expectedSelectionStart == 0) { 224 assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(retrievalLength, 0))); 225 } else { 226 assertEquals(expectedString.subSequence(0, expectedSelectionStart).toString(), 227 ic.getTextBeforeCursor(retrievalLength, 0).toString()); 228 } 229 if (expectedSelectionStart == expectedSelectionEnd) { 230 assertTrue(TextUtils.isEmpty(ic.getSelectedText(0))); // null is allowed. 231 } else { 232 assertEquals(expectedString.subSequence(expectedSelectionStart, 233 expectedSelectionEnd).toString(), ic.getSelectedText(0).toString()); 234 } 235 if (expectedSelectionEnd == expectedString.length()) { 236 assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(retrievalLength, 0))); 237 } else { 238 assertEquals(expectedString.subSequence(expectedSelectionEnd, 239 expectedString.length()).toString(), 240 ic.getTextAfterCursor(retrievalLength, 0).toString()); 241 } 242 } 243 244 /** 245 * Tests {@link BaseInputConnection#deleteSurroundingText(int, int)} comprehensively. 246 */ 247 @Test 248 public void testDeleteSurroundingText() { 249 verifyDeleteSurroundingTextMain("012[]3456789", 0, 0, "012[]3456789"); 250 verifyDeleteSurroundingTextMain("012[]3456789", -1, -1, "012[]3456789"); 251 verifyDeleteSurroundingTextMain("012[]3456789", 1, 2, "01[]56789"); 252 verifyDeleteSurroundingTextMain("012[]3456789", 10, 1, "[]456789"); 253 verifyDeleteSurroundingTextMain("012[]3456789", 1, 10, "01[]"); 254 verifyDeleteSurroundingTextMain("[]0123456789", 3, 3, "[]3456789"); 255 verifyDeleteSurroundingTextMain("0123456789[]", 3, 3, "0123456[]"); 256 verifyDeleteSurroundingTextMain("012[345]6789", 0, 0, "012[345]6789"); 257 verifyDeleteSurroundingTextMain("012[345]6789", -1, -1, "012[345]6789"); 258 verifyDeleteSurroundingTextMain("012[345]6789", 1, 2, "01[345]89"); 259 verifyDeleteSurroundingTextMain("012[345]6789", 10, 1, "[345]789"); 260 verifyDeleteSurroundingTextMain("012[345]6789", 1, 10, "01[345]"); 261 verifyDeleteSurroundingTextMain("[012]3456789", 3, 3, "[012]6789"); 262 verifyDeleteSurroundingTextMain("0123456[789]", 3, 3, "0123[789]"); 263 verifyDeleteSurroundingTextMain("[0123456789]", 0, 0, "[0123456789]"); 264 verifyDeleteSurroundingTextMain("[0123456789]", 1, 1, "[0123456789]"); 265 266 // Surrogate characters do not have any special meanings. Validating the character sequence 267 // is beyond the goal of this API. 268 verifyDeleteSurroundingTextMain("0<>[]3456789", 1, 0, "0<[]3456789"); 269 verifyDeleteSurroundingTextMain("0<>[]3456789", 2, 0, "0[]3456789"); 270 verifyDeleteSurroundingTextMain("0<>[]3456789", 3, 0, "[]3456789"); 271 verifyDeleteSurroundingTextMain("012[]<>56789", 0, 1, "012[]>56789"); 272 verifyDeleteSurroundingTextMain("012[]<>56789", 0, 2, "012[]56789"); 273 verifyDeleteSurroundingTextMain("012[]<>56789", 0, 3, "012[]6789"); 274 verifyDeleteSurroundingTextMain("0<<[]3456789", 1, 0, "0<[]3456789"); 275 verifyDeleteSurroundingTextMain("0<<[]3456789", 2, 0, "0[]3456789"); 276 verifyDeleteSurroundingTextMain("0<<[]3456789", 3, 0, "[]3456789"); 277 verifyDeleteSurroundingTextMain("012[]<<56789", 0, 1, "012[]<56789"); 278 verifyDeleteSurroundingTextMain("012[]<<56789", 0, 2, "012[]56789"); 279 verifyDeleteSurroundingTextMain("012[]<<56789", 0, 3, "012[]6789"); 280 verifyDeleteSurroundingTextMain("0>>[]3456789", 1, 0, "0>[]3456789"); 281 verifyDeleteSurroundingTextMain("0>>[]3456789", 2, 0, "0[]3456789"); 282 verifyDeleteSurroundingTextMain("0>>[]3456789", 3, 0, "[]3456789"); 283 verifyDeleteSurroundingTextMain("012[]>>56789", 0, 1, "012[]>56789"); 284 verifyDeleteSurroundingTextMain("012[]>>56789", 0, 2, "012[]56789"); 285 verifyDeleteSurroundingTextMain("012[]>>56789", 0, 3, "012[]6789"); 286 } 287 288 private static void verifyDeleteSurroundingTextInCodePointsMain(String initialState, 289 int deleteBeforeInCodePoints, int deleteAfterInCodePoints, String expectedState) { 290 final CharSequence source = InputConnectionTestUtils.formatString(initialState); 291 final BaseInputConnection ic = createConnectionWithSelection(source); 292 ic.deleteSurroundingTextInCodePoints(deleteBeforeInCodePoints, deleteAfterInCodePoints); 293 294 final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState); 295 final int expectedSelectionStart = Selection.getSelectionStart(expectedString); 296 final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString); 297 298 // It is sufficient to check the surrounding text up to source.length() characters, because 299 // InputConnection.deleteSurroundingTextInCodePoints() is not supposed to increase the text 300 // length. 301 final int retrievalLength = source.length(); 302 if (expectedSelectionStart == 0) { 303 assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(retrievalLength, 0))); 304 } else { 305 assertEquals(expectedString.subSequence(0, expectedSelectionStart).toString(), 306 ic.getTextBeforeCursor(retrievalLength, 0).toString()); 307 } 308 if (expectedSelectionStart == expectedSelectionEnd) { 309 assertTrue(TextUtils.isEmpty(ic.getSelectedText(0))); // null is allowed. 310 } else { 311 assertEquals(expectedString.subSequence(expectedSelectionStart, 312 expectedSelectionEnd).toString(), ic.getSelectedText(0).toString()); 313 } 314 if (expectedSelectionEnd == expectedString.length()) { 315 assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(retrievalLength, 0))); 316 } else { 317 assertEquals(expectedString.subSequence(expectedSelectionEnd, 318 expectedString.length()).toString(), 319 ic.getTextAfterCursor(retrievalLength, 0).toString()); 320 } 321 } 322 323 /** 324 * Tests {@link BaseInputConnection#deleteSurroundingTextInCodePoints(int, int)} 325 * comprehensively. 326 */ 327 @Test 328 public void testDeleteSurroundingTextInCodePoints() { 329 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 0, 0, "012[]3456789"); 330 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", -1, -1, "012[]3456789"); 331 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 2, "01[]56789"); 332 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 10, 1, "[]456789"); 333 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 10, "01[]"); 334 verifyDeleteSurroundingTextInCodePointsMain("[]0123456789", 3, 3, "[]3456789"); 335 verifyDeleteSurroundingTextInCodePointsMain("0123456789[]", 3, 3, "0123456[]"); 336 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 0, 0, "012[345]6789"); 337 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", -1, -1, "012[345]6789"); 338 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 2, "01[345]89"); 339 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 10, 1, "[345]789"); 340 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 10, "01[345]"); 341 verifyDeleteSurroundingTextInCodePointsMain("[012]3456789", 3, 3, "[012]6789"); 342 verifyDeleteSurroundingTextInCodePointsMain("0123456[789]", 3, 3, "0123[789]"); 343 verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 0, 0, "[0123456789]"); 344 verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 1, 1, "[0123456789]"); 345 346 verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 1, 0, "0[]3456789"); 347 verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 2, 0, "[]3456789"); 348 verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 3, 0, "[]3456789"); 349 verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 1, "012[]56789"); 350 verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 2, "012[]6789"); 351 verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 3, "012[]789"); 352 353 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 0, "[]<><><><><>"); 354 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1, "[]<><><><>"); 355 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 2, "[]<><><>"); 356 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 3, "[]<><>"); 357 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 4, "[]<>"); 358 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 5, "[]"); 359 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 6, "[]"); 360 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1000, "[]"); 361 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 0, 0, "<><><><><>[]"); 362 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1, 0, "<><><><>[]"); 363 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 2, 0, "<><><>[]"); 364 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 3, 0, "<><>[]"); 365 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 4, 0, "<>[]"); 366 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 5, 0, "[]"); 367 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 6, 0, "[]"); 368 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1000, 0, "[]"); 369 370 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 0, "0<<[]3456789"); 371 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 0, "0<<[]3456789"); 372 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 0, "0<<[]3456789"); 373 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 1, "012[]<<56789"); 374 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 2, "012[]<<56789"); 375 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 3, "012[]<<56789"); 376 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 0, "0>>[]3456789"); 377 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 0, "0>>[]3456789"); 378 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 0, "0>>[]3456789"); 379 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 1, "012[]>>56789"); 380 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 2, "012[]>>56789"); 381 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 3, "012[]>>56789"); 382 verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 0, "01<[]>456789"); 383 verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 0, 1, "01<[]>456789"); 384 verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 1, 0, "<1[]3456789"); 385 verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 2, 0, "<[]3456789"); 386 verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 3, 0, "<12[]3456789"); 387 verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 1, 0, "<[]3456789"); 388 verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 2, 0, "<<>[]3456789"); 389 verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 3, 0, "<<>[]3456789"); 390 verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 1, "012[]4>6789"); 391 verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 2, "012[]>6789"); 392 verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 3, "012[]34>6789"); 393 verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 1, "012[]>6789"); 394 verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 2, "012[]<>>6789"); 395 verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 3, "012[]<>>6789"); 396 397 // Atomicity test. 398 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 1, "0<<[]3456789"); 399 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 1, "0<<[]3456789"); 400 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 1, "0<<[]3456789"); 401 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 1, "012[]<<56789"); 402 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 2, "012[]<<56789"); 403 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 3, "012[]<<56789"); 404 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 1, "0>>[]3456789"); 405 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 1, "0>>[]3456789"); 406 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 1, "0>>[]3456789"); 407 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 1, "012[]>>56789"); 408 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 2, "012[]>>56789"); 409 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 3, "012[]>>56789"); 410 verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 1, "01<[]>456789"); 411 412 // Do not verify the character sequences in the selected region. 413 verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 0, "0[><]456789"); 414 verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 0, 1, "01[><]56789"); 415 verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 1, "0[><]56789"); 416 } 417 418 @Test 419 public void testCloseConnection() { 420 final BaseInputConnection connection = createBaseInputConnection(); 421 422 final CharSequence source = "0123456789"; 423 connection.commitText(source, source.length()); 424 connection.setComposingRegion(2, 5); 425 final Editable text = connection.getEditable(); 426 assertEquals(2, BaseInputConnection.getComposingSpanStart(text)); 427 assertEquals(5, BaseInputConnection.getComposingSpanEnd(text)); 428 429 // BaseInputConnection#closeConnection() must clear the on-going composition. 430 connection.closeConnection(); 431 assertEquals(-1, BaseInputConnection.getComposingSpanStart(text)); 432 assertEquals(-1, BaseInputConnection.getComposingSpanEnd(text)); 433 } 434 435 @Test 436 public void testGetHandler() { 437 final BaseInputConnection connection = createBaseInputConnection(); 438 439 // BaseInputConnection must not implement getHandler(). 440 assertNull(connection.getHandler()); 441 } 442 443 @Test 444 public void testCommitContent() { 445 final BaseInputConnection connection = createBaseInputConnection(); 446 447 final InputContentInfo inputContentInfo = new InputContentInfo( 448 Uri.parse("content://com.example/path"), 449 new ClipDescription("sample content", new String[]{"image/png"}), 450 Uri.parse("https://example.com")); 451 // The default implementation should do nothing and just return false. 452 assertFalse(connection.commitContent(inputContentInfo, 0 /* flags */, null /* opts */)); 453 } 454 455 @Test 456 public void testGetSelectedText_wrongSelection() { 457 final BaseInputConnection connection = createBaseInputConnection(); 458 Editable editable = connection.getEditable(); 459 editable.append("hello"); 460 editable.setSpan(Selection.SELECTION_START, 4, 4, Spanned.SPAN_POINT_POINT); 461 editable.removeSpan(Selection.SELECTION_END); 462 463 // Should not crash. 464 connection.getSelectedText(0); 465 } 466 } 467