Skip to content

Commit

Permalink
Fix double delete on backspace on Android (flutter#22626)
Browse files Browse the repository at this point in the history
  • Loading branch information
gspencergoog committed Nov 20, 2020
1 parent b502fcb commit b4d4e30
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 167 deletions.
Expand Up @@ -296,21 +296,7 @@ public boolean sendKeyEvent(KeyEvent event) {
}

if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
int selStart = clampIndexToEditable(Selection.getSelectionStart(mEditable), mEditable);
int selEnd = clampIndexToEditable(Selection.getSelectionEnd(mEditable), mEditable);
if (selStart == selEnd && selStart > 0) {
// Extend selection to left of the last character
selStart = flutterTextUtils.getOffsetBefore(mEditable, selStart);
}
if (selEnd > selStart) {
// Delete the selection.
Selection.setSelection(mEditable, selStart);
mEditable.delete(selStart, selEnd);
return true;
}
return false;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart == selEnd && !event.isShiftPressed()) {
Expand Down
Expand Up @@ -1029,166 +1029,18 @@ public void testCursorAnchorInfo() {
}

@Test
public void testSendKeyEvent_delKeyDeletesBackward() {
public void testSendKeyEvent_delKeyNotConsumed() {
int selStart = 29;
ListenableEditingState editable = sampleEditable(selStart, selStart, SAMPLE_RTL_TEXT);
InputConnectionAdaptor adaptor = sampleInputConnectionAdaptor(editable);

KeyEvent downKeyDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);

for (int i = 0; i < 9; i++) {
for (int i = 0; i < 10; i++) {
boolean didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
}
assertEquals(Selection.getSelectionStart(editable), 19);

for (int i = 0; i < 9; i++) {
boolean didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
}
assertEquals(Selection.getSelectionStart(editable), 10);
}

@Test
public void testSendKeyEvent_delKeyDeletesBackwardComplexEmojis() {
int selStart = 75;
ListenableEditingState editable = sampleEditable(selStart, selStart, SAMPLE_EMOJI_TEXT);
InputConnectionAdaptor adaptor = sampleInputConnectionAdaptor(editable);

KeyEvent downKeyDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
boolean didConsume;

// Normal Character
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 74);

// Non-Spacing Mark
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 73);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 72);

// Keycap
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 69);

// Keycap with invalid base
adaptor.setSelection(68, 68);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 66);
adaptor.setSelection(67, 67);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 66);

// Zero Width Joiner
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 55);

// Zero Width Joiner with invalid base
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 53);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 52);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 51);

// ----- Start Emoji Tag Sequence with invalid base testing ----
// Delete base tag
adaptor.setSelection(39, 39);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 37);

// Delete the sequence
adaptor.setSelection(49, 49);
for (int i = 0; i < 6; i++) {
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertFalse(didConsume);
}
assertEquals(Selection.getSelectionStart(editable), 37);
// ----- End Emoji Tag Sequence with invalid base testing ----

// Emoji Tag Sequence
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 23);

// Variation Selector with invalid base
adaptor.setSelection(22, 22);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 21);
adaptor.setSelection(22, 22);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 21);

// Variation Selector
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 19);

// Emoji Modifier
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 16);

// Emoji Modifier with invalid base
adaptor.setSelection(14, 14);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 13);
adaptor.setSelection(14, 14);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 13);

// Line Feed
adaptor.setSelection(12, 12);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 11);

// Carriage Return
adaptor.setSelection(12, 12);
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 11);

// Carriage Return and Line Feed
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 9);

// Regional Indicator Symbol odd
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 7);

// Regional Indicator Symbol even
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 3);

// Simple Emoji
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 1);

// First CodePoint
didConsume = adaptor.sendKeyEvent(downKeyDown);
assertTrue(didConsume);
assertEquals(Selection.getSelectionStart(editable), 0);
assertEquals(29, Selection.getSelectionStart(editable));
}

@Test
Expand Down

0 comments on commit b4d4e30

Please sign in to comment.