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 com.google.common.truth.Truth.assertThat; 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.assertTrue; 26 import static org.junit.Assert.fail; 27 import static org.testng.Assert.expectThrows; 28 29 import android.content.ClipDescription; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.platform.test.annotations.AppModeSdkSandbox; 33 import android.text.Editable; 34 import android.text.Selection; 35 import android.text.Spannable; 36 import android.text.SpannableString; 37 import android.text.Spanned; 38 import android.text.TextUtils; 39 import android.view.inputmethod.BaseInputConnection; 40 import android.view.inputmethod.CompletionInfo; 41 import android.view.inputmethod.ExtractedTextRequest; 42 import android.view.inputmethod.InputConnection; 43 import android.view.inputmethod.InputContentInfo; 44 import android.view.inputmethod.InputMethodManager; 45 import android.view.inputmethod.SurroundingText; 46 import android.view.inputmethod.TextSnapshot; 47 import android.view.inputmethod.cts.util.InputConnectionTestUtils; 48 49 import androidx.annotation.NonNull; 50 import androidx.test.ext.junit.runners.AndroidJUnit4; 51 import androidx.test.filters.MediumTest; 52 import androidx.test.platform.app.InstrumentationRegistry; 53 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 57 @MediumTest 58 @RunWith(AndroidJUnit4.class) 59 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 60 public class BaseInputConnectionTest { 61 62 private static final int CAPS_MODE_MASK = TextUtils.CAP_MODE_CHARACTERS 63 | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES; 64 65 private static final int MEMORY_EFFICIENT_TEXT_LENGTH = 2048; 66 67 // Retrieve a large range to text to verify the content. 68 private static final int TEXT_LENGTH_TO_RETRIEVAL = 1024; 69 70 @Test testDefaultMethods()71 public void testDefaultMethods() { 72 // These methods are default to return fixed result. 73 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 74 75 assertFalse(connection.beginBatchEdit()); 76 assertFalse(connection.endBatchEdit()); 77 78 // only fit for test default implementation of commitCompletion. 79 int completionId = 1; 80 String completionString = "commitCompletion test"; 81 assertFalse(connection.commitCompletion(new CompletionInfo(completionId, 82 0, completionString))); 83 84 assertNull(connection.getExtractedText(new ExtractedTextRequest(), 0)); 85 86 // only fit for test default implementation of performEditorAction. 87 int actionCode = 1; 88 int actionId = 2; 89 String action = "android.intent.action.MAIN"; 90 assertTrue(connection.performEditorAction(actionCode)); 91 assertFalse(connection.performContextMenuAction(actionId)); 92 assertFalse(connection.performPrivateCommand(action, new Bundle())); 93 } 94 95 @Test testOpComposingSpans()96 public void testOpComposingSpans() { 97 Spannable text = new SpannableString("Test ComposingSpans"); 98 BaseInputConnection.setComposingSpans(text); 99 assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1); 100 assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1); 101 BaseInputConnection.removeComposingSpans(text); 102 assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1); 103 assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1); 104 } 105 106 /** 107 * getEditable: Return the target of edit operations. The default implementation 108 * returns its own fake editable that is just used for composing text. 109 * clearMetaKeyStates: Default implementation uses 110 * MetaKeyKeyListener#clearMetaKeyState(long, int) to clear the state. 111 * BugId:1738511 112 * commitText: Default implementation replaces any existing composing text with the given 113 * text. 114 * deleteSurroundingText: The default implementation performs the deletion around the current 115 * selection position of the editable text. 116 * getCursorCapsMode: The default implementation uses TextUtils.getCapsMode to get the 117 * cursor caps mode for the current selection position in the editable text. 118 * TextUtils.getCapsMode is tested fully in TextUtilsTest#testGetCapsMode. 119 * getTextBeforeCursor, getTextAfterCursor: The default implementation performs the deletion 120 * around the current selection position of the editable text. 121 * setSelection: changes the selection position in the current editable text. 122 */ 123 @Test testOpTextMethods()124 public void testOpTextMethods() { 125 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 126 127 // return is an default Editable instance with empty source 128 final Editable text = connection.getEditable(); 129 assertNotNull(text); 130 assertEquals(0, text.length()); 131 132 // Test commitText, not default fake mode 133 CharSequence str = "TestCommit "; 134 Editable inputText = Editable.Factory.getInstance().newEditable(str); 135 connection.commitText(inputText, inputText.length()); 136 final Editable text2 = connection.getEditable(); 137 int strLength = str.length(); 138 assertEquals(strLength, text2.length()); 139 assertEquals(str.toString(), text2.toString()); 140 assertEquals(TextUtils.CAP_MODE_WORDS, 141 connection.getCursorCapsMode(TextUtils.CAP_MODE_WORDS)); 142 int offLength = 3; 143 CharSequence expected = str.subSequence(strLength - offLength, strLength); 144 assertEquals(expected.toString(), connection.getTextBeforeCursor(offLength, 145 BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 146 connection.setSelection(0, 0); 147 expected = str.subSequence(0, offLength); 148 assertEquals(expected.toString(), connection.getTextAfterCursor(offLength, 149 BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 150 151 // Test deleteSurroundingText 152 int end = text2.length(); 153 connection.setSelection(end, end); 154 // Delete the ending space 155 assertTrue(connection.deleteSurroundingText(1, 2)); 156 Editable text3 = connection.getEditable(); 157 assertEquals(strLength - 1, text3.length()); 158 String expectedDelString = "TestCommit"; 159 assertEquals(expectedDelString, text3.toString()); 160 } 161 162 /** 163 * finishComposingText: The default implementation removes the composing state from the 164 * current editable text. 165 * setComposingText: The default implementation places the given text into the editable, 166 * replacing any existing composing text 167 */ 168 @Test testFinishComposingText()169 public void testFinishComposingText() { 170 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 171 CharSequence str = "TestFinish"; 172 Editable inputText = Editable.Factory.getInstance().newEditable(str); 173 connection.commitText(inputText, inputText.length()); 174 final Editable text = connection.getEditable(); 175 // Test finishComposingText, not default fake mode 176 BaseInputConnection.setComposingSpans(text); 177 assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1); 178 assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1); 179 connection.finishComposingText(); 180 assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1); 181 assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1); 182 } 183 184 /** 185 * Updates InputMethodManager with the current fullscreen mode. 186 */ 187 @Test testReportFullscreenMode()188 public void testReportFullscreenMode() { 189 final InputMethodManager imm = InstrumentationRegistry.getInstrumentation() 190 .getTargetContext().getSystemService(InputMethodManager.class); 191 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 192 connection.reportFullscreenMode(false); 193 assertFalse(imm.isFullscreenMode()); 194 connection.reportFullscreenMode(true); 195 // Only IMEs are allowed to report full-screen mode. Calling this method from the 196 // application should have no effect. 197 assertFalse(imm.isFullscreenMode()); 198 } 199 verifyDeleteSurroundingTextMain( final String initialState, final int deleteBefore, final int deleteAfter, final String expectedState)200 private void verifyDeleteSurroundingTextMain( 201 final String initialState, 202 final int deleteBefore, 203 final int deleteAfter, 204 final String expectedState) { 205 verifyDeleteSurroundingTextMain(initialState, deleteBefore, deleteAfter, expectedState, 206 false /* clearSelection */); 207 } 208 verifyDeleteSurroundingTextMain( final String initialState, final int deleteBefore, final int deleteAfter, final String expectedState, final boolean clearSelection)209 private void verifyDeleteSurroundingTextMain( 210 final String initialState, 211 final int deleteBefore, 212 final int deleteAfter, 213 final String expectedState, 214 final boolean clearSelection) { 215 final CharSequence source = clearSelection ? initialState 216 : InputConnectionTestUtils.formatString(initialState); 217 final BaseInputConnection ic = 218 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 219 220 if (clearSelection) { 221 Selection.removeSelection(ic.getEditable()); 222 } 223 224 final boolean result = ic.deleteSurroundingText(deleteBefore, deleteAfter); 225 if (!result) { 226 assertEquals(expectedState, ic.getEditable().toString()); 227 return; 228 } else if (clearSelection) { 229 fail("deleteSurroundingText should return false for invalid selection"); 230 } 231 232 final CharSequence expectedString = 233 clearSelection 234 ? expectedState 235 : InputConnectionTestUtils.formatString(expectedState); 236 final int expectedSelectionStart = Selection.getSelectionStart(expectedString); 237 final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString); 238 verifyTextAndSelection(ic, expectedString, expectedSelectionStart, expectedSelectionEnd); 239 } 240 241 /** 242 * Tests {@link BaseInputConnection#deleteSurroundingText(int, int)} comprehensively. 243 */ 244 @Test testDeleteSurroundingText()245 public void testDeleteSurroundingText() { 246 verifyDeleteSurroundingTextMain("0123456789", 1, 2, "0123456789", 247 true /* clearSelection*/); 248 verifyDeleteSurroundingTextMain("012[]3456789", 0, 0, "012[]3456789"); 249 verifyDeleteSurroundingTextMain("012[]3456789", -1, -1, "012[]3456789"); 250 verifyDeleteSurroundingTextMain("012[]3456789", 1, 2, "01[]56789"); 251 verifyDeleteSurroundingTextMain("012[]3456789", 10, 1, "[]456789"); 252 verifyDeleteSurroundingTextMain("012[]3456789", 1, 10, "01[]"); 253 verifyDeleteSurroundingTextMain("[]0123456789", 3, 3, "[]3456789"); 254 verifyDeleteSurroundingTextMain("0123456789[]", 3, 3, "0123456[]"); 255 verifyDeleteSurroundingTextMain("012[345]6789", 0, 0, "012[345]6789"); 256 verifyDeleteSurroundingTextMain("012[345]6789", -1, -1, "012[345]6789"); 257 verifyDeleteSurroundingTextMain("012[345]6789", 1, 2, "01[345]89"); 258 verifyDeleteSurroundingTextMain("012[345]6789", 10, 1, "[345]789"); 259 verifyDeleteSurroundingTextMain("012[345]6789", 1, 10, "01[345]"); 260 verifyDeleteSurroundingTextMain("[012]3456789", 3, 3, "[012]6789"); 261 verifyDeleteSurroundingTextMain("0123456[789]", 3, 3, "0123[789]"); 262 verifyDeleteSurroundingTextMain("[0123456789]", 0, 0, "[0123456789]"); 263 verifyDeleteSurroundingTextMain("[0123456789]", 1, 1, "[0123456789]"); 264 265 // Surrogate characters do not have any special meanings. Validating the character sequence 266 // is beyond the goal of this API. 267 verifyDeleteSurroundingTextMain("0<>[]3456789", 1, 0, "0<[]3456789"); 268 verifyDeleteSurroundingTextMain("0<>[]3456789", 2, 0, "0[]3456789"); 269 verifyDeleteSurroundingTextMain("0<>[]3456789", 3, 0, "[]3456789"); 270 verifyDeleteSurroundingTextMain("012[]<>56789", 0, 1, "012[]>56789"); 271 verifyDeleteSurroundingTextMain("012[]<>56789", 0, 2, "012[]56789"); 272 verifyDeleteSurroundingTextMain("012[]<>56789", 0, 3, "012[]6789"); 273 verifyDeleteSurroundingTextMain("0<<[]3456789", 1, 0, "0<[]3456789"); 274 verifyDeleteSurroundingTextMain("0<<[]3456789", 2, 0, "0[]3456789"); 275 verifyDeleteSurroundingTextMain("0<<[]3456789", 3, 0, "[]3456789"); 276 verifyDeleteSurroundingTextMain("012[]<<56789", 0, 1, "012[]<56789"); 277 verifyDeleteSurroundingTextMain("012[]<<56789", 0, 2, "012[]56789"); 278 verifyDeleteSurroundingTextMain("012[]<<56789", 0, 3, "012[]6789"); 279 verifyDeleteSurroundingTextMain("0>>[]3456789", 1, 0, "0>[]3456789"); 280 verifyDeleteSurroundingTextMain("0>>[]3456789", 2, 0, "0[]3456789"); 281 verifyDeleteSurroundingTextMain("0>>[]3456789", 3, 0, "[]3456789"); 282 verifyDeleteSurroundingTextMain("012[]>>56789", 0, 1, "012[]>56789"); 283 verifyDeleteSurroundingTextMain("012[]>>56789", 0, 2, "012[]56789"); 284 verifyDeleteSurroundingTextMain("012[]>>56789", 0, 3, "012[]6789"); 285 } 286 verifyDeleteSurroundingTextInCodePointsMain( String initialState, int deleteBeforeInCodePoints, int deleteAfterInCodePoints, String expectedState)287 private void verifyDeleteSurroundingTextInCodePointsMain( 288 String initialState, 289 int deleteBeforeInCodePoints, 290 int deleteAfterInCodePoints, 291 String expectedState) { 292 final CharSequence source = InputConnectionTestUtils.formatString(initialState); 293 final BaseInputConnection ic = 294 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 295 ic.deleteSurroundingTextInCodePoints(deleteBeforeInCodePoints, deleteAfterInCodePoints); 296 297 final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState); 298 final int expectedSelectionStart = Selection.getSelectionStart(expectedString); 299 final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString); 300 verifyTextAndSelection(ic, expectedString, expectedSelectionStart, expectedSelectionEnd); 301 } 302 303 /** 304 * Tests {@link BaseInputConnection#deleteSurroundingTextInCodePoints(int, int)} 305 * comprehensively. 306 */ 307 @Test testDeleteSurroundingTextInCodePoints()308 public void testDeleteSurroundingTextInCodePoints() { 309 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 0, 0, "012[]3456789"); 310 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", -1, -1, "012[]3456789"); 311 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 2, "01[]56789"); 312 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 10, 1, "[]456789"); 313 verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 10, "01[]"); 314 verifyDeleteSurroundingTextInCodePointsMain("[]0123456789", 3, 3, "[]3456789"); 315 verifyDeleteSurroundingTextInCodePointsMain("0123456789[]", 3, 3, "0123456[]"); 316 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 0, 0, "012[345]6789"); 317 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", -1, -1, "012[345]6789"); 318 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 2, "01[345]89"); 319 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 10, 1, "[345]789"); 320 verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 10, "01[345]"); 321 verifyDeleteSurroundingTextInCodePointsMain("[012]3456789", 3, 3, "[012]6789"); 322 verifyDeleteSurroundingTextInCodePointsMain("0123456[789]", 3, 3, "0123[789]"); 323 verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 0, 0, "[0123456789]"); 324 verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 1, 1, "[0123456789]"); 325 326 verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 1, 0, "0[]3456789"); 327 verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 2, 0, "[]3456789"); 328 verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 3, 0, "[]3456789"); 329 verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 1, "012[]56789"); 330 verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 2, "012[]6789"); 331 verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 3, "012[]789"); 332 333 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 0, "[]<><><><><>"); 334 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1, "[]<><><><>"); 335 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 2, "[]<><><>"); 336 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 3, "[]<><>"); 337 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 4, "[]<>"); 338 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 5, "[]"); 339 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 6, "[]"); 340 verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1000, "[]"); 341 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 0, 0, "<><><><><>[]"); 342 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1, 0, "<><><><>[]"); 343 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 2, 0, "<><><>[]"); 344 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 3, 0, "<><>[]"); 345 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 4, 0, "<>[]"); 346 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 5, 0, "[]"); 347 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 6, 0, "[]"); 348 verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1000, 0, "[]"); 349 350 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 0, "0<<[]3456789"); 351 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 0, "0<<[]3456789"); 352 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 0, "0<<[]3456789"); 353 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 1, "012[]<<56789"); 354 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 2, "012[]<<56789"); 355 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 3, "012[]<<56789"); 356 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 0, "0>>[]3456789"); 357 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 0, "0>>[]3456789"); 358 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 0, "0>>[]3456789"); 359 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 1, "012[]>>56789"); 360 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 2, "012[]>>56789"); 361 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 3, "012[]>>56789"); 362 verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 0, "01<[]>456789"); 363 verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 0, 1, "01<[]>456789"); 364 verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 1, 0, "<1[]3456789"); 365 verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 2, 0, "<[]3456789"); 366 verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 3, 0, "<12[]3456789"); 367 verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 1, 0, "<[]3456789"); 368 verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 2, 0, "<<>[]3456789"); 369 verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 3, 0, "<<>[]3456789"); 370 verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 1, "012[]4>6789"); 371 verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 2, "012[]>6789"); 372 verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 3, "012[]34>6789"); 373 verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 1, "012[]>6789"); 374 verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 2, "012[]<>>6789"); 375 verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 3, "012[]<>>6789"); 376 377 // Atomicity test. 378 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 1, "0<<[]3456789"); 379 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 1, "0<<[]3456789"); 380 verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 1, "0<<[]3456789"); 381 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 1, "012[]<<56789"); 382 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 2, "012[]<<56789"); 383 verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 3, "012[]<<56789"); 384 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 1, "0>>[]3456789"); 385 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 1, "0>>[]3456789"); 386 verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 1, "0>>[]3456789"); 387 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 1, "012[]>>56789"); 388 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 2, "012[]>>56789"); 389 verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 3, "012[]>>56789"); 390 verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 1, "01<[]>456789"); 391 392 // Do not verify the character sequences in the selected region. 393 verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 0, "0[><]456789"); 394 verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 0, 1, "01[><]56789"); 395 verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 1, "0[><]56789"); 396 } 397 398 @Test testCloseConnection()399 public void testCloseConnection() { 400 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 401 402 final CharSequence source = "0123456789"; 403 connection.commitText(source, source.length()); 404 connection.setComposingRegion(2, 5); 405 final Editable text = connection.getEditable(); 406 assertEquals(2, BaseInputConnection.getComposingSpanStart(text)); 407 assertEquals(5, BaseInputConnection.getComposingSpanEnd(text)); 408 409 // BaseInputConnection#closeConnection() must clear the on-going composition. 410 connection.closeConnection(); 411 assertEquals(-1, BaseInputConnection.getComposingSpanStart(text)); 412 assertEquals(-1, BaseInputConnection.getComposingSpanEnd(text)); 413 } 414 415 @Test testGetHandler()416 public void testGetHandler() { 417 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 418 419 // BaseInputConnection must not implement getHandler(). 420 assertNull(connection.getHandler()); 421 } 422 423 @Test testCommitContent()424 public void testCommitContent() { 425 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 426 427 final InputContentInfo inputContentInfo = 428 new InputContentInfo( 429 Uri.parse("content://com.example/path"), 430 new ClipDescription("sample content", new String[] {"image/png"}), 431 Uri.parse("https://example.com")); 432 // The default implementation should do nothing and just return false. 433 assertFalse(connection.commitContent(inputContentInfo, 0 /* flags */, null /* opts */)); 434 } 435 436 @Test testGetSelectedText_wrongSelection()437 public void testGetSelectedText_wrongSelection() { 438 final BaseInputConnection connection = InputConnectionTestUtils.createBaseInputConnection(); 439 Editable editable = connection.getEditable(); 440 editable.append("hello"); 441 editable.setSpan(Selection.SELECTION_START, 4, 4, Spanned.SPAN_POINT_POINT); 442 editable.removeSpan(Selection.SELECTION_END); 443 444 // Should not crash. 445 connection.getSelectedText(0); 446 } 447 448 @Test testGetSurroundingText_hasTextBeforeSelection()449 public void testGetSurroundingText_hasTextBeforeSelection() { 450 // 123456789| 451 final CharSequence source = InputConnectionTestUtils.formatString("123456789[]"); 452 final BaseInputConnection connection = 453 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 454 455 // 9| 456 SurroundingText surroundingText1 = connection.getSurroundingText(1, 1, 0); 457 assertEquals("9", surroundingText1.getText().toString()); 458 assertEquals(1, surroundingText1.getSelectionEnd()); 459 assertEquals(1, surroundingText1.getSelectionEnd()); 460 assertEquals(8, surroundingText1.getOffset()); 461 462 // 123456789| 463 SurroundingText surroundingText2 = connection.getSurroundingText(10, 1, 0); 464 assertEquals("123456789", surroundingText2.getText().toString()); 465 assertEquals(9, surroundingText2.getSelectionStart()); 466 assertEquals(9, surroundingText2.getSelectionEnd()); 467 assertEquals(0, surroundingText2.getOffset()); 468 469 // | 470 SurroundingText surroundingText3 = connection.getSurroundingText(0, 10, 471 BaseInputConnection.GET_TEXT_WITH_STYLES); 472 assertEquals("", surroundingText3.getText().toString()); 473 assertEquals(0, surroundingText3.getSelectionStart()); 474 assertEquals(0, surroundingText3.getSelectionEnd()); 475 assertEquals(9, surroundingText3.getOffset()); 476 } 477 478 @Test testGetSurroundingText_hasTextAfterSelection()479 public void testGetSurroundingText_hasTextAfterSelection() { 480 // |123456789 481 final CharSequence source = InputConnectionTestUtils.formatString("[]123456789"); 482 final BaseInputConnection connection = 483 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 484 485 // |1 486 SurroundingText surroundingText1 = connection.getSurroundingText(1, 1, 487 BaseInputConnection.GET_TEXT_WITH_STYLES); 488 assertEquals("1", surroundingText1.getText().toString()); 489 assertEquals(0, surroundingText1.getSelectionStart()); 490 assertEquals(0, surroundingText1.getSelectionEnd()); 491 assertEquals(0, surroundingText1.getOffset()); 492 493 // | 494 SurroundingText surroundingText2 = connection.getSurroundingText(10, 1, 0); 495 assertEquals("1", surroundingText2.getText().toString()); 496 assertEquals(0, surroundingText2.getSelectionStart()); 497 assertEquals(0, surroundingText2.getSelectionEnd()); 498 assertEquals(0, surroundingText2.getOffset()); 499 500 // |123456789 501 SurroundingText surroundingText3 = connection.getSurroundingText(0, 10, 0); 502 assertEquals("123456789", surroundingText3.getText().toString()); 503 assertEquals(0, surroundingText3.getSelectionStart()); 504 assertEquals(0, surroundingText3.getSelectionEnd()); 505 assertEquals(0, surroundingText3.getOffset()); 506 } 507 508 @Test testGetSurroundingText_hasSelection()509 public void testGetSurroundingText_hasSelection() { 510 // 123|45|6789 511 final CharSequence source = InputConnectionTestUtils.formatString("123[45]6789"); 512 final BaseInputConnection connection = 513 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 514 515 // 3|45|6 516 SurroundingText surroundingText1 = connection.getSurroundingText(1, 1, 0); 517 assertEquals("3456", surroundingText1.getText().toString()); 518 assertEquals(1, surroundingText1.getSelectionStart()); 519 assertEquals(3, surroundingText1.getSelectionEnd()); 520 assertEquals(2, surroundingText1.getOffset()); 521 522 // 123|45|6 523 SurroundingText surroundingText2 = connection.getSurroundingText(10, 1, 524 BaseInputConnection.GET_TEXT_WITH_STYLES); 525 assertEquals("123456", surroundingText2.getText().toString()); 526 assertEquals(3, surroundingText2.getSelectionStart()); 527 assertEquals(5, surroundingText2.getSelectionEnd()); 528 assertEquals(0, surroundingText2.getOffset()); 529 530 // |45|6789 531 SurroundingText surroundingText3 = connection.getSurroundingText(0, 10, 0); 532 assertEquals("456789", surroundingText3.getText().toString()); 533 assertEquals(0, surroundingText3.getSelectionStart()); 534 assertEquals(2, surroundingText3.getSelectionEnd()); 535 assertEquals(3, surroundingText3.getOffset()); 536 537 // 123|45|6789 538 SurroundingText surroundingText4 = connection.getSurroundingText(10, 10, 539 BaseInputConnection.GET_TEXT_WITH_STYLES); 540 assertEquals("123456789", surroundingText4.getText().toString()); 541 assertEquals(3, surroundingText4.getSelectionStart()); 542 assertEquals(5, surroundingText4.getSelectionEnd()); 543 assertEquals(0, surroundingText4.getOffset()); 544 545 // |45| 546 SurroundingText surroundingText5 = 547 connection.getSurroundingText(0, 0, BaseInputConnection.GET_TEXT_WITH_STYLES); 548 assertEquals("45", surroundingText5.getText().toString()); 549 assertEquals(0, surroundingText5.getSelectionStart()); 550 assertEquals(2, surroundingText5.getSelectionEnd()); 551 assertEquals(3, surroundingText5.getOffset()); 552 } 553 554 @Test testInvalidGetTextBeforeOrAfterCursorRequest()555 public void testInvalidGetTextBeforeOrAfterCursorRequest() { 556 final CharSequence source = InputConnectionTestUtils.formatString("hello[]"); 557 final BaseInputConnection connection = 558 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 559 560 // getTextBeforeCursor 561 assertEquals("", connection.getTextBeforeCursor(0, 0).toString()); 562 assertEquals("", connection.getTextBeforeCursor( 563 0, BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 564 assertEquals("hello", connection.getTextBeforeCursor(10, 0).toString()); 565 assertEquals("hello", connection.getTextBeforeCursor( 566 100, BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 567 expectThrows(IllegalArgumentException.class, ()-> connection.getTextBeforeCursor(-1, 0)); 568 569 // getTextAfterCursor 570 assertEquals("", connection.getTextAfterCursor(0, 0).toString()); 571 assertEquals("", connection.getTextAfterCursor( 572 0, BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 573 assertEquals("", connection.getTextAfterCursor(100, 0).toString()); 574 assertEquals("", connection.getTextAfterCursor( 575 100, BaseInputConnection.GET_TEXT_WITH_STYLES).toString()); 576 expectThrows(IllegalArgumentException.class, ()-> connection.getTextAfterCursor(-1, 0)); 577 } 578 579 @Test testTakeSnapshot()580 public void testTakeSnapshot() { 581 final BaseInputConnection connection = 582 InputConnectionTestUtils.createBaseInputConnectionWithSelection( 583 InputConnectionTestUtils.formatString("0123[456]789")); 584 585 verifyTextSnapshot(connection); 586 587 connection.setSelection(10, 10); 588 verifyTextSnapshot(connection); 589 590 connection.setComposingRegion(3, 10); 591 verifyTextSnapshot(connection); 592 593 connection.finishComposingText(); 594 verifyTextSnapshot(connection); 595 } 596 597 @Test testTakeSnapshotForNoSelection()598 public void testTakeSnapshotForNoSelection() { 599 final BaseInputConnection connection = 600 InputConnectionTestUtils.createBaseInputConnection( 601 Editable.Factory.getInstance().newEditable("test")); 602 // null should be returned for text with no selection. 603 assertThat(connection.takeSnapshot()).isNull(); 604 } 605 verifyTextSnapshot(@onNull BaseInputConnection connection)606 private void verifyTextSnapshot(@NonNull BaseInputConnection connection) { 607 final Editable editable = connection.getEditable(); 608 609 final TextSnapshot snapshot = connection.takeSnapshot(); 610 assertThat(snapshot).isNotNull(); 611 assertThat(snapshot.getSelectionStart()).isEqualTo(Selection.getSelectionStart(editable)); 612 assertThat(snapshot.getSelectionEnd()).isEqualTo(Selection.getSelectionEnd(editable)); 613 assertThat(snapshot.getCompositionStart()) 614 .isEqualTo(BaseInputConnection.getComposingSpanStart(editable)); 615 assertThat(snapshot.getCompositionEnd()) 616 .isEqualTo(BaseInputConnection.getComposingSpanEnd(editable)); 617 assertThat(snapshot.getCursorCapsMode()) 618 .isEqualTo(connection.getCursorCapsMode(CAPS_MODE_MASK)); 619 final SurroundingText surroundingText = snapshot.getSurroundingText(); 620 assertThat(surroundingText).isNotNull(); 621 final SurroundingText expectedSurroundingText = 622 connection.getSurroundingText( 623 MEMORY_EFFICIENT_TEXT_LENGTH / 2, 624 MEMORY_EFFICIENT_TEXT_LENGTH / 2, 625 InputConnection.GET_TEXT_WITH_STYLES); 626 assertThat( 627 surroundingText 628 .getText() 629 .toString() 630 .contentEquals(expectedSurroundingText.getText())) 631 .isTrue(); 632 assertThat(surroundingText.getSelectionStart()) 633 .isEqualTo(expectedSurroundingText.getSelectionStart()); 634 assertThat(surroundingText.getSelectionStart()) 635 .isEqualTo(expectedSurroundingText.getSelectionStart()); 636 assertThat(surroundingText.getSelectionEnd()) 637 .isEqualTo(expectedSurroundingText.getSelectionEnd()); 638 assertThat(surroundingText.getOffset()).isEqualTo(expectedSurroundingText.getOffset()); 639 assertThat(snapshot.getCursorCapsMode()) 640 .isEqualTo(connection.getCursorCapsMode(CAPS_MODE_MASK)); 641 } 642 643 @Test testReplaceText()644 public void testReplaceText() { 645 verifyReplaceText("012[3456]789", 3, 7, "text", 1, "012text[]789"); 646 verifyReplaceText("012[]3456789", 0, 3, "text", 1, "text[]3456789"); 647 verifyReplaceText("012[]3456789", 3, 0, "text", 1, "text[]3456789"); 648 verifyReplaceText("012[]3456789", 0, 10, "text", 1, "text[]"); 649 verifyReplaceText("0123456789[]", 0, 3, "text", -1, "[]text3456789"); 650 verifyReplaceText("0123456789[]", 10, 10, "text", 1, "0123456789text[]"); 651 verifyReplaceText("0123456789[]", 100, 100, "text", 1, "0123456789text[]"); 652 verifyReplaceText("[]0123456789", 0, 0, "text", 1, "text[]0123456789"); 653 verifyReplaceText("[]0123456789", 0, 5, "text", 1, "text[]56789"); 654 verifyReplaceText("[]0123456789", 0, 10, "text", -1, "[]text"); 655 verifyReplaceText("[0123456789]", 0, 10, "text", 1, "text[]"); 656 verifyReplaceText("[0123456789]", 0, 8, "text", 1, "text[]89"); 657 } 658 verifyReplaceText( final String initialState, final int start, final int end, final String text, final int newCursorPosition, final String expectedState)659 private static void verifyReplaceText( 660 final String initialState, 661 final int start, 662 final int end, 663 final String text, 664 final int newCursorPosition, 665 final String expectedState) { 666 final CharSequence source = InputConnectionTestUtils.formatString(initialState); 667 final BaseInputConnection ic = 668 InputConnectionTestUtils.createBaseInputConnectionWithSelection(source); 669 assertTrue(ic.replaceText(start, end, text, newCursorPosition, null)); 670 final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState); 671 final int expectedSelectionStart = Selection.getSelectionStart(expectedString); 672 final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString); 673 verifyTextAndSelection(ic, expectedString, expectedSelectionStart, expectedSelectionEnd); 674 } 675 verifyTextAndSelection( BaseInputConnection ic, final CharSequence expectedString, final int expectedSelectionStart, final int expectedSelectionEnd)676 private static void verifyTextAndSelection( 677 BaseInputConnection ic, 678 final CharSequence expectedString, 679 final int expectedSelectionStart, 680 final int expectedSelectionEnd) { 681 if (expectedSelectionStart == 0) { 682 assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(TEXT_LENGTH_TO_RETRIEVAL, 0))); 683 } else { 684 assertEquals( 685 expectedString.subSequence(0, expectedSelectionStart).toString(), 686 ic.getTextBeforeCursor(TEXT_LENGTH_TO_RETRIEVAL, 0).toString()); 687 } 688 if (expectedSelectionStart == expectedSelectionEnd) { 689 assertTrue(TextUtils.isEmpty(ic.getSelectedText(0))); // null is allowed. 690 } else { 691 assertEquals( 692 expectedString 693 .subSequence(expectedSelectionStart, expectedSelectionEnd) 694 .toString(), 695 ic.getSelectedText(0).toString()); 696 } 697 if (expectedSelectionEnd == expectedString.length()) { 698 assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(TEXT_LENGTH_TO_RETRIEVAL, 0))); 699 } else { 700 assertEquals( 701 expectedString 702 .subSequence(expectedSelectionEnd, expectedString.length()) 703 .toString(), 704 ic.getTextAfterCursor(TEXT_LENGTH_TO_RETRIEVAL, 0).toString()); 705 } 706 } 707 } 708