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.text.method.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 23 import android.os.SystemClock; 24 import android.support.test.filters.MediumTest; 25 import android.support.test.runner.AndroidJUnit4; 26 import android.text.Editable; 27 import android.text.InputType; 28 import android.text.Selection; 29 import android.text.Spannable; 30 import android.text.SpannableStringBuilder; 31 import android.text.method.BaseKeyListener; 32 import android.view.KeyCharacterMap; 33 import android.view.KeyEvent; 34 import android.widget.TextView.BufferType; 35 36 import com.android.compatibility.common.util.CtsKeyEventUtil; 37 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 41 /** 42 * Test {@link android.text.method.BaseKeyListener}. 43 */ 44 @MediumTest 45 @RunWith(AndroidJUnit4.class) 46 public class BaseKeyListenerTest extends KeyListenerTestCase { 47 private static final CharSequence TEST_STRING = "123456"; 48 49 @Test 50 public void testBackspace() throws Throwable { 51 verifyBackspace(0); 52 } 53 54 private void verifyBackspace(int modifiers) throws Throwable { 55 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 56 final KeyEvent event = getKey(KeyEvent.KEYCODE_DEL, modifiers); 57 Editable content = Editable.Factory.getInstance().newEditable(TEST_STRING); 58 59 // Nothing to delete when the cursor is at the beginning. 60 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 61 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 62 assertEquals("123456", content.toString()); 63 64 // Delete the first three letters using a selection. 65 prepTextViewSync(content, mockBaseKeyListener, false, 0, 3); 66 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 67 assertEquals("456", content.toString()); 68 69 // Delete the character prior to the cursor when there's no selection 70 prepTextViewSync(content, mockBaseKeyListener, false, 2, 2); 71 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 72 assertEquals("46", content.toString()); 73 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 74 assertEquals("6", content.toString()); 75 76 // The deletion works on a Logical direction basis in RTL text.. 77 String testText = "\u05E9\u05DC\u05D5\u05DD\u002E"; 78 content = Editable.Factory.getInstance().newEditable(testText); 79 80 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 81 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 82 assertEquals(testText, content.toString()); 83 84 int end = testText.length(); 85 prepTextViewSync(content, mockBaseKeyListener, false, end, end); 86 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 87 assertEquals("\u05E9\u05DC\u05D5\u05DD", content.toString()); 88 89 int middle = testText.length() / 2; 90 prepTextViewSync(content, mockBaseKeyListener, false, middle, middle); 91 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 92 assertEquals("\u05E9\u05D5\u05DD", content.toString()); 93 94 // And in BiDi text 95 testText = "\u05D6\u05D4\u0020Android\u0020\u05E2\u05D5\u05D1\u05D3"; 96 content = Editable.Factory.getInstance().newEditable(testText); 97 98 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 99 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 100 assertEquals(content.toString(), content.toString()); 101 102 end = testText.length(); 103 prepTextViewSync(content, mockBaseKeyListener, false, end, end); 104 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 105 assertEquals("\u05D6\u05D4\u0020Android\u0020\u05E2\u05D5\u05D1", content.toString()); 106 107 prepTextViewSync(content, mockBaseKeyListener, false, 6, 6); 108 mockBaseKeyListener.backspace(mTextView, content, event.getKeyCode(), event); 109 assertEquals("\u05D6\u05D4\u0020Anroid\u0020\u05E2\u05D5\u05D1", content.toString()); 110 } 111 112 @Test 113 public void testBackspace_withShift() throws Throwable { 114 verifyBackspace(KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON); 115 } 116 117 @Test 118 public void testBackspace_withAlt() throws Throwable { 119 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 120 Editable content = Editable.Factory.getInstance().newEditable(TEST_STRING); 121 122 // Delete the entire line with ALT + DEL, even if we're at the head... 123 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 124 executeAltBackspace(content, mockBaseKeyListener); 125 assertEquals("", content.toString()); 126 127 // ...or the tail... 128 content = Editable.Factory.getInstance().newEditable(TEST_STRING); 129 final int end = TEST_STRING.length(); 130 prepTextViewSync(content, mockBaseKeyListener, false, end, end); 131 executeAltBackspace(content, mockBaseKeyListener); 132 assertEquals("", content.toString()); 133 134 // ...or somewhere in the middle. 135 content = Editable.Factory.getInstance().newEditable(TEST_STRING); 136 final int middle = end / 2; 137 prepTextViewSync(content, mockBaseKeyListener, false, middle, middle); 138 executeAltBackspace(content, mockBaseKeyListener); 139 assertEquals("", content.toString()); 140 } 141 142 @Test 143 public void testBackspace_withSendKeys() throws Throwable { 144 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 145 146 // Delete the first character '1' 147 prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 1, 1); 148 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 149 assertEquals("23456", mTextView.getText().toString()); 150 151 // Delete character '2' and '3' 152 prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 1, 3); 153 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 154 assertEquals("1456", mTextView.getText().toString()); 155 156 // Delete everything on the line the cursor is on. 157 prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 0, 0); 158 CtsKeyEventUtil.sendKeyWhileHoldingModifier( 159 mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_ALT_LEFT); 160 assertEquals("", mTextView.getText().toString()); 161 162 // ALT+DEL deletes the selection only. 163 prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 2, 4); 164 CtsKeyEventUtil.sendKeyWhileHoldingModifier( 165 mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_ALT_LEFT); 166 assertEquals("1256", mTextView.getText().toString()); 167 168 // DEL key does not take effect when TextView does not have BaseKeyListener. 169 prepTextViewSync(TEST_STRING, null, true, 1, 1); 170 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 171 assertEquals(TEST_STRING, mTextView.getText().toString()); 172 } 173 174 private void verifyCursorPosition(Editable content, int offset) { 175 assertEquals(offset, Selection.getSelectionStart(content)); 176 assertEquals(offset, Selection.getSelectionEnd(content)); 177 } 178 179 @Test 180 public void testBackspace_withCtrl() throws Throwable { 181 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 182 183 // If the contents only having symbolic characters, delete all characters. 184 String testText = "!#$%&'()`{*}_?+"; 185 Editable content = Editable.Factory.getInstance().newEditable(testText); 186 prepTextViewSync(content, mockBaseKeyListener, false, testText.length(), testText.length()); 187 executeCtrlBackspace(content, mockBaseKeyListener); 188 assertEquals("", content.toString()); 189 verifyCursorPosition(content, 0); 190 191 // Latin ASCII text 192 testText = "Hello, World. This is Android."; 193 content = Editable.Factory.getInstance().newEditable(testText); 194 195 // If the cursor is head of the text, should do nothing. 196 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 197 executeCtrlBackspace(content, mockBaseKeyListener); 198 assertEquals("Hello, World. This is Android.", content.toString()); 199 verifyCursorPosition(content, 0); 200 201 prepTextViewSync(content, mockBaseKeyListener, false, testText.length(), testText.length()); 202 executeCtrlBackspace(content, mockBaseKeyListener); 203 assertEquals("Hello, World. This is ", content.toString()); 204 verifyCursorPosition(content, content.toString().length()); 205 206 executeCtrlBackspace(content, mockBaseKeyListener); 207 assertEquals("Hello, World. This ", content.toString()); 208 verifyCursorPosition(content, content.toString().length()); 209 210 executeCtrlBackspace(content, mockBaseKeyListener); 211 assertEquals("Hello, World. ", content.toString()); 212 verifyCursorPosition(content, content.toString().length()); 213 214 executeCtrlBackspace(content, mockBaseKeyListener); 215 assertEquals("Hello, ", content.toString()); 216 verifyCursorPosition(content, content.toString().length()); 217 218 executeCtrlBackspace(content, mockBaseKeyListener); 219 assertEquals("", content.toString()); 220 verifyCursorPosition(content, 0); 221 222 executeCtrlBackspace(content, mockBaseKeyListener); 223 assertEquals("", content.toString()); 224 verifyCursorPosition(content, 0); 225 226 // Latin ASCII, cursor is middle of the text. 227 testText = "Hello, World. This is Android."; 228 content = Editable.Factory.getInstance().newEditable(testText); 229 int charsFromTail = 12; // Cursor location is 12 chars from the tail.(before "is"). 230 prepTextViewSync(content, mockBaseKeyListener, false, 231 testText.length() - charsFromTail, testText.length() - charsFromTail); 232 233 executeCtrlBackspace(content, mockBaseKeyListener); 234 assertEquals("Hello, World. is Android.", content.toString()); 235 verifyCursorPosition(content, content.toString().length() - charsFromTail); 236 237 executeCtrlBackspace(content, mockBaseKeyListener); 238 assertEquals("Hello, is Android.", content.toString()); 239 verifyCursorPosition(content, content.toString().length() - charsFromTail); 240 241 executeCtrlBackspace(content, mockBaseKeyListener); 242 assertEquals(" is Android.", content.toString()); 243 verifyCursorPosition(content, 0); 244 245 executeCtrlBackspace(content, mockBaseKeyListener); 246 assertEquals(" is Android.", content.toString()); 247 verifyCursorPosition(content, 0); 248 249 // Latin ASCII, cursor is inside word. 250 testText = "Hello, World. This is Android."; 251 content = Editable.Factory.getInstance().newEditable(testText); 252 charsFromTail = 14; // Cursor location is 12 chars from the tail. (inside "This") 253 prepTextViewSync(content, mockBaseKeyListener, false, 254 testText.length() - charsFromTail, testText.length() - charsFromTail); 255 256 257 executeCtrlBackspace(content, mockBaseKeyListener); 258 assertEquals("Hello, World. is is Android.", content.toString()); 259 verifyCursorPosition(content, content.toString().length() - charsFromTail); 260 261 executeCtrlBackspace(content, mockBaseKeyListener); 262 assertEquals("Hello, is is Android.", content.toString()); 263 verifyCursorPosition(content, content.toString().length() - charsFromTail); 264 265 executeCtrlBackspace(content, mockBaseKeyListener); 266 assertEquals("is is Android.", content.toString()); 267 verifyCursorPosition(content, 0); 268 269 executeCtrlBackspace(content, mockBaseKeyListener); 270 assertEquals("is is Android.", content.toString()); 271 verifyCursorPosition(content, 0); 272 273 // Hebrew Text 274 // The deletion works on a Logical direction basis. 275 testText = "\u05E9\u05DC\u05D5\u05DD\u0020\u05D4\u05E2\u05D5\u05DC\u05DD\u002E\u0020" + 276 "\u05D6\u05D4\u0020\u05D0\u05E0\u05D3\u05E8\u05D5\u05D0\u05D9\u05D3\u002E"; 277 content = Editable.Factory.getInstance().newEditable(testText); 278 prepTextViewSync(content, mockBaseKeyListener, false, testText.length(), testText.length()); 279 280 executeCtrlBackspace(content, mockBaseKeyListener); 281 assertEquals("\u05E9\u05DC\u05D5\u05DD\u0020\u05D4\u05E2\u05D5\u05DC\u05DD\u002E\u0020" + 282 "\u05D6\u05D4\u0020", content.toString()); 283 verifyCursorPosition(content, content.toString().length()); 284 285 executeCtrlBackspace(content, mockBaseKeyListener); 286 assertEquals("\u05E9\u05DC\u05D5\u05DD\u0020\u05D4\u05E2\u05D5\u05DC\u05DD\u002E\u0020", 287 content.toString()); 288 verifyCursorPosition(content, content.toString().length()); 289 290 executeCtrlBackspace(content, mockBaseKeyListener); 291 assertEquals("\u05E9\u05DC\u05D5\u05DD\u0020", content.toString()); 292 verifyCursorPosition(content, content.toString().length()); 293 294 executeCtrlBackspace(content, mockBaseKeyListener); 295 assertEquals("", content.toString()); 296 verifyCursorPosition(content, 0); 297 298 executeCtrlBackspace(content, mockBaseKeyListener); 299 assertEquals("", content.toString()); 300 verifyCursorPosition(content, 0); 301 302 // BiDi Text 303 // The deletion works on a Logical direction basis. 304 testText = "\u05D6\u05D4\u0020\u05DC\u002D\u0020\u0041Android\u0020\u05E2\u05D5\u05D1" + 305 "\u05D3\u0020\u05D4\u05D9\u05D8\u05D1\u002E"; 306 content = Editable.Factory.getInstance().newEditable(testText); 307 prepTextViewSync(content, mockBaseKeyListener, false, testText.length(), testText.length()); 308 309 executeCtrlBackspace(content, mockBaseKeyListener); 310 assertEquals("\u05D6\u05D4\u0020\u05DC\u002D\u0020\u0041Android\u0020\u05E2\u05D5\u05D1" + 311 "\u05D3\u0020", content.toString()); 312 verifyCursorPosition(content, content.toString().length()); 313 314 executeCtrlBackspace(content, mockBaseKeyListener); 315 assertEquals("\u05D6\u05D4\u0020\u05DC\u002D\u0020\u0041Android\u0020", content.toString()); 316 verifyCursorPosition(content, content.toString().length()); 317 318 executeCtrlBackspace(content, mockBaseKeyListener); 319 assertEquals("\u05D6\u05D4\u0020\u05DC\u002D\u0020", content.toString()); 320 verifyCursorPosition(content, content.toString().length()); 321 322 executeCtrlBackspace(content, mockBaseKeyListener); 323 assertEquals("\u05D6\u05D4\u0020", content.toString()); 324 verifyCursorPosition(content, content.toString().length()); 325 326 executeCtrlBackspace(content, mockBaseKeyListener); 327 assertEquals("", content.toString()); 328 verifyCursorPosition(content, 0); 329 330 executeCtrlBackspace(content, mockBaseKeyListener); 331 assertEquals("", content.toString()); 332 verifyCursorPosition(content, 0); 333 } 334 335 @Test 336 public void testForwardDelete_withCtrl() throws Throwable { 337 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 338 339 // If the contents only having symbolic characters, delete all characters. 340 String testText = "!#$%&'()`{*}_?+"; 341 Editable content = Editable.Factory.getInstance().newEditable(testText); 342 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 343 executeCtrlForwardDelete(content, mockBaseKeyListener); 344 assertEquals("", content.toString()); 345 verifyCursorPosition(content, 0); 346 347 // Latin ASCII text 348 testText = "Hello, World. This is Android."; 349 content = Editable.Factory.getInstance().newEditable(testText); 350 351 // If the cursor is tail of the text, should do nothing. 352 prepTextViewSync(content, mockBaseKeyListener, false, testText.length(), testText.length()); 353 executeCtrlForwardDelete(content, mockBaseKeyListener); 354 assertEquals("Hello, World. This is Android.", content.toString()); 355 verifyCursorPosition(content, testText.length()); 356 357 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 358 executeCtrlForwardDelete(content, mockBaseKeyListener); 359 assertEquals(", World. This is Android.", content.toString()); 360 verifyCursorPosition(content, 0); 361 362 executeCtrlForwardDelete(content, mockBaseKeyListener); 363 assertEquals(". This is Android.", content.toString()); 364 verifyCursorPosition(content, 0); 365 366 executeCtrlForwardDelete(content, mockBaseKeyListener); 367 assertEquals(" is Android.", content.toString()); 368 verifyCursorPosition(content, 0); 369 370 executeCtrlForwardDelete(content, mockBaseKeyListener); 371 assertEquals(" Android.", content.toString()); 372 verifyCursorPosition(content, 0); 373 374 executeCtrlForwardDelete(content, mockBaseKeyListener); 375 assertEquals(".", content.toString()); 376 verifyCursorPosition(content, 0); 377 378 executeCtrlForwardDelete(content, mockBaseKeyListener); 379 assertEquals("", content.toString()); 380 verifyCursorPosition(content, 0); 381 382 executeCtrlForwardDelete(content, mockBaseKeyListener); 383 assertEquals("", content.toString()); 384 verifyCursorPosition(content, 0); 385 386 // Latin ASCII, cursor is middle of the text. 387 testText = "Hello, World. This is Android."; 388 content = Editable.Factory.getInstance().newEditable(testText); 389 int charsFromHead = 14; // Cursor location is 14 chars from the head.(before "This"). 390 prepTextViewSync(content, mockBaseKeyListener, false, charsFromHead, charsFromHead); 391 392 executeCtrlForwardDelete(content, mockBaseKeyListener); 393 assertEquals("Hello, World. is Android.", content.toString()); 394 verifyCursorPosition(content, charsFromHead); 395 396 executeCtrlForwardDelete(content, mockBaseKeyListener); 397 assertEquals("Hello, World. Android.", content.toString()); 398 verifyCursorPosition(content, charsFromHead); 399 400 executeCtrlForwardDelete(content, mockBaseKeyListener); 401 assertEquals("Hello, World. .", content.toString()); 402 verifyCursorPosition(content, charsFromHead); 403 404 executeCtrlForwardDelete(content, mockBaseKeyListener); 405 assertEquals("Hello, World. ", content.toString()); 406 verifyCursorPosition(content, charsFromHead); 407 408 executeCtrlForwardDelete(content, mockBaseKeyListener); 409 assertEquals("Hello, World. ", content.toString()); 410 verifyCursorPosition(content, charsFromHead); 411 412 // Latin ASCII, cursor is inside word. 413 testText = "Hello, World. This is Android."; 414 content = Editable.Factory.getInstance().newEditable(testText); 415 charsFromHead = 16; // Cursor location is 16 chars from the head. (inside "This") 416 prepTextViewSync(content, mockBaseKeyListener, false, charsFromHead, charsFromHead); 417 418 executeCtrlForwardDelete(content, mockBaseKeyListener); 419 assertEquals("Hello, World. Th is Android.", content.toString()); 420 verifyCursorPosition(content, charsFromHead); 421 422 executeCtrlForwardDelete(content, mockBaseKeyListener); 423 assertEquals("Hello, World. Th Android.", content.toString()); 424 verifyCursorPosition(content, charsFromHead); 425 426 executeCtrlForwardDelete(content, mockBaseKeyListener); 427 assertEquals("Hello, World. Th.", content.toString()); 428 verifyCursorPosition(content, charsFromHead); 429 430 executeCtrlForwardDelete(content, mockBaseKeyListener); 431 assertEquals("Hello, World. Th", content.toString()); 432 verifyCursorPosition(content, charsFromHead); 433 434 executeCtrlForwardDelete(content, mockBaseKeyListener); 435 assertEquals("Hello, World. Th", content.toString()); 436 verifyCursorPosition(content, charsFromHead); 437 438 // Hebrew Text 439 // The deletion works on a Logical direction basis. 440 testText = "\u05E9\u05DC\u05D5\u05DD\u0020\u05D4\u05E2\u05D5\u05DC\u05DD\u002E\u0020" + 441 "\u05D6\u05D4\u0020\u05D0\u05E0\u05D3\u05E8\u05D5\u05D0\u05D9\u05D3\u002E"; 442 content = Editable.Factory.getInstance().newEditable(testText); 443 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 444 445 executeCtrlForwardDelete(content, mockBaseKeyListener); 446 assertEquals("\u0020\u05D4\u05E2\u05D5\u05DC\u05DD\u002E\u0020\u05D6\u05D4\u0020\u05D0" + 447 "\u05E0\u05D3\u05E8\u05D5\u05D0\u05D9\u05D3\u002E", content.toString()); 448 verifyCursorPosition(content, 0); 449 450 executeCtrlForwardDelete(content, mockBaseKeyListener); 451 assertEquals("\u002E\u0020\u05D6\u05D4\u0020\u05D0\u05E0\u05D3\u05E8\u05D5\u05D0\u05D9" + 452 "\u05D3\u002E", content.toString()); 453 verifyCursorPosition(content, 0); 454 455 executeCtrlForwardDelete(content, mockBaseKeyListener); 456 assertEquals("\u0020\u05D0\u05E0\u05D3\u05E8\u05D5\u05D0\u05D9\u05D3\u002E", 457 content.toString()); 458 verifyCursorPosition(content, 0); 459 460 executeCtrlForwardDelete(content, mockBaseKeyListener); 461 assertEquals("\u002E", content.toString()); 462 verifyCursorPosition(content, 0); 463 464 executeCtrlForwardDelete(content, mockBaseKeyListener); 465 assertEquals("", content.toString()); 466 verifyCursorPosition(content, 0); 467 468 executeCtrlForwardDelete(content, mockBaseKeyListener); 469 assertEquals("", content.toString()); 470 verifyCursorPosition(content, 0); 471 472 // BiDi Text 473 // The deletion works on a Logical direction basis. 474 testText = "\u05D6\u05D4\u0020\u05DC\u002D\u0020\u0041Android\u0020\u05E2\u05D5\u05D1" + 475 "\u05D3\u0020\u05D4\u05D9\u05D8\u05D1\u002E"; 476 content = Editable.Factory.getInstance().newEditable(testText); 477 prepTextViewSync(content, mockBaseKeyListener, false, 0, 0); 478 479 executeCtrlForwardDelete(content, mockBaseKeyListener); 480 assertEquals("\u0020\u05DC\u002D\u0020\u0041Android\u0020\u05E2\u05D5\u05D1\u05D3\u0020" + 481 "\u05D4\u05D9\u05D8\u05D1\u002E", content.toString()); 482 verifyCursorPosition(content, 0); 483 484 executeCtrlForwardDelete(content, mockBaseKeyListener); 485 assertEquals("\u002D\u0020\u0041Android\u0020\u05E2\u05D5\u05D1\u05D3\u0020\u05D4\u05D9" + 486 "\u05D8\u05D1\u002E", content.toString()); 487 verifyCursorPosition(content, 0); 488 489 executeCtrlForwardDelete(content, mockBaseKeyListener); 490 assertEquals("\u0020\u05E2\u05D5\u05D1\u05D3\u0020\u05D4\u05D9\u05D8\u05D1\u002E", 491 content.toString()); 492 verifyCursorPosition(content, 0); 493 494 executeCtrlForwardDelete(content, mockBaseKeyListener); 495 assertEquals("\u0020\u05D4\u05D9\u05D8\u05D1\u002E", content.toString()); 496 verifyCursorPosition(content, 0); 497 498 executeCtrlForwardDelete(content, mockBaseKeyListener); 499 assertEquals("\u002E", content.toString()); 500 verifyCursorPosition(content, 0); 501 502 executeCtrlForwardDelete(content, mockBaseKeyListener); 503 assertEquals("", content.toString()); 504 verifyCursorPosition(content, 0); 505 506 executeCtrlForwardDelete(content, mockBaseKeyListener); 507 assertEquals("", content.toString()); 508 verifyCursorPosition(content, 0); 509 } 510 511 /* 512 * Check point: 513 * 1. Press 0 key, the content of TextView does not changed. 514 * 2. Set a selection and press DEL key, the selection is deleted. 515 * 3. ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting the event's text into the content. 516 */ 517 @Test 518 public void testPressKey() throws Throwable { 519 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 520 521 // press '0' key. 522 prepTextViewSync(TEST_STRING, mockBaseKeyListener, true, 0, 0); 523 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_0); 524 assertEquals("123456", mTextView.getText().toString()); 525 526 // delete character '2' 527 prepTextViewSync(mTextView.getText(), mockBaseKeyListener, true, 1, 2); 528 CtsKeyEventUtil.sendKeys(mInstrumentation, mTextView, KeyEvent.KEYCODE_DEL); 529 assertEquals("13456", mTextView.getText().toString()); 530 531 // test ACTION_MULTIPLE KEYCODE_UNKNOWN key event. 532 KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), "abcd", 533 KeyCharacterMap.BUILT_IN_KEYBOARD, 0); 534 prepTextViewSync(mTextView.getText(), mockBaseKeyListener, true, 2, 2); 535 CtsKeyEventUtil.sendKey(mInstrumentation, mTextView, event); 536 mInstrumentation.waitForIdleSync(); 537 // the text of TextView is never changed, onKeyOther never works. 538 // assertEquals("13abcd456", mTextView.getText().toString()); 539 } 540 541 @Test 542 public void testOnKeyOther() { 543 final BaseKeyListener mockBaseKeyListener = new MockBaseKeyListener(); 544 final String string = "abc"; 545 final SpannableStringBuilder content = new SpannableStringBuilder(string); 546 547 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN); 548 assertFalse(mockBaseKeyListener.onKeyOther(mTextView, content, event)); 549 assertEquals(string, content.toString()); 550 551 event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_0); 552 assertFalse(mockBaseKeyListener.onKeyOther(mTextView, content, event)); 553 assertEquals(string, content.toString()); 554 555 Selection.setSelection(content, 1, 0); 556 event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_UNKNOWN); 557 assertFalse(mockBaseKeyListener.onKeyOther(mTextView, content, event)); 558 assertEquals(string, content.toString()); 559 560 event = new KeyEvent(SystemClock.uptimeMillis(), "b", 0, 0); 561 assertTrue(mockBaseKeyListener.onKeyOther(mTextView, content, event)); 562 assertEquals("bbc", content.toString()); 563 } 564 565 private void executeAltBackspace(Editable content, BaseKeyListener listener) { 566 final KeyEvent delKeyEvent = getKey(KeyEvent.KEYCODE_DEL, 567 KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON); 568 listener.backspace(mTextView, content, KeyEvent.KEYCODE_DEL, delKeyEvent); 569 } 570 571 private void executeCtrlBackspace(Editable content, BaseKeyListener listener) { 572 final KeyEvent delKeyEvent = getKey(KeyEvent.KEYCODE_DEL, 573 KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); 574 listener.backspace(mTextView, content, KeyEvent.KEYCODE_DEL, delKeyEvent); 575 } 576 577 private void executeCtrlForwardDelete(Editable content, BaseKeyListener listener) { 578 final KeyEvent delKeyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL, 579 KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); 580 listener.forwardDelete(mTextView, content, KeyEvent.KEYCODE_FORWARD_DEL, delKeyEvent); 581 } 582 583 /** 584 * Prepares mTextView state for tests by synchronously setting the content and key listener, on 585 * the UI thread. 586 */ 587 private void prepTextViewSync(final CharSequence content, final BaseKeyListener keyListener, 588 final boolean selectInTextView, final int selectionStart, final int selectionEnd) 589 throws Throwable { 590 mActivityRule.runOnUiThread(() -> { 591 mTextView.setText(content, BufferType.EDITABLE); 592 mTextView.setKeyListener(keyListener); 593 Selection.setSelection( 594 selectInTextView ? mTextView.getText() : (Spannable) content, 595 selectionStart, selectionEnd); 596 }); 597 mInstrumentation.waitForIdleSync(); 598 assertTrue(mTextView.hasWindowFocus()); 599 } 600 601 /** 602 * A mocked {@link android.text.method.BaseKeyListener} for testing purposes. 603 */ 604 private class MockBaseKeyListener extends BaseKeyListener { 605 public int getInputType() { 606 return InputType.TYPE_CLASS_DATETIME 607 | InputType.TYPE_DATETIME_VARIATION_DATE; 608 } 609 } 610 } 611