19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text.method; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1923a67678e3848e2465518769236139e6cafac2eeSeigo Nonakaimport android.graphics.Paint; 20ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.icu.lang.UCharacter; 21ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.icu.lang.UProperty; 224037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Editable; 234037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Emoji; 244037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.InputType; 254037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Layout; 264037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.NoCopySpan; 274037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Selection; 284037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Spanned; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener.Capitalize; 30ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.style.ReplacementSpan; 314037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.view.KeyEvent; 324037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.view.View; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.TextView; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3523a67678e3848e2465518769236139e6cafac2eeSeigo Nonakaimport com.android.internal.annotations.GuardedBy; 3623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka 37f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonakaimport java.text.BreakIterator; 38f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 396b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown/** 406b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Abstract base class for key listeners. 416b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * 426b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Provides a basic foundation for entering and editing text. 436b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert 446b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * characters as keys are pressed. 45405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * <p></p> 46405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * As for all implementations of {@link KeyListener}, this class is only concerned 47405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * with hardware keyboards. Software input methods have no obligation to trigger 48405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * the methods in this class. 496b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown */ 506b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brownpublic abstract class BaseKeyListener extends MetaKeyKeyListener 516b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown implements KeyListener { 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete(); 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 544d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka private static final int LINE_FEED = 0x0A; 554d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka private static final int CARRIAGE_RETURN = 0x0D; 564d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka 5723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka private final Object mLock = new Object(); 5823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka 5923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka @GuardedBy("mLock") 6023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka static Paint sCachedPaint = null; 6123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_DEL} key in 6414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * a {@link TextView}. If there is a selection, deletes the selection; otherwise, 6514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * deletes the character before the cursor, if any; ALT+DEL deletes everything on 6614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * the line the cursor is on. 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 686b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * @return true if anything was deleted; false otherwise. 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) { 71d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown return backspaceOrForwardDelete(view, content, keyCode, event, false); 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown /** 7514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_FORWARD_DEL} 7614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * key in a {@link TextView}. If there is a selection, deletes the selection; otherwise, 7714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * deletes the character before the cursor, if any; ALT+FORWARD_DEL deletes everything on 7814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * the line the cursor is on. 7914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * 8014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown * @return true if anything was deleted; false otherwise. 8114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown */ 8214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown public boolean forwardDelete(View view, Editable content, int keyCode, KeyEvent event) { 83d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown return backspaceOrForwardDelete(view, content, keyCode, event, true); 84d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } 85d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown 86ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Returns true if the given code point is a variation selector. 87ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static boolean isVariationSelector(int codepoint) { 88ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return UCharacter.hasBinaryProperty(codepoint, UProperty.VARIATION_SELECTOR); 89ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 90ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 91ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Returns the offset of the replacement span edge if the offset is inside of the replacement 92ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // span. Otherwise, does nothing and returns the input offset value. 93ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static int adjustReplacementSpan(CharSequence text, int offset, boolean moveToStart) { 94ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (!(text instanceof Spanned)) { 95ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return offset; 96ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 97ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 98ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset, ReplacementSpan.class); 99ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka for (int i = 0; i < spans.length; i++) { 100ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int start = ((Spanned) text).getSpanStart(spans[i]); 101ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int end = ((Spanned) text).getSpanEnd(spans[i]); 102ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 103ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (start < offset && end > offset) { 104ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka offset = moveToStart ? start : end; 105ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 106ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 107ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return offset; 108ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 109ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 110ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Returns the start offset to be deleted by a backspace key from the given offset. 111ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static int getOffsetForBackspaceKey(CharSequence text, int offset) { 112ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (offset <= 1) { 113ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return 0; 114ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 115ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 116ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Initial state 117ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int STATE_START = 0; 118ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 1194d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka // The offset is immediately before line feed. 1204d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_LF = 1; 1214d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka 122ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before a KEYCAP. 1234d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_KEYCAP = 2; 124ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before a variation selector and a KEYCAP. 1254d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_VS_AND_KEYCAP = 3; 126ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 127ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before an emoji modifier. 1284d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_EMOJI_MODIFIER = 4; 129ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before a variation selector and an emoji modifier. 1304d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_VS_AND_EMOJI_MODIFIER = 5; 131ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 132ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before a variation selector. 1334d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_VS = 6; 134ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 135bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka // The offset is immediately before an emoji. 136bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka final int STATE_BEFORE_EMOJI = 7; 137ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before a ZWJ that were seen before a ZWJ emoji. 1384d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_ZWJ = 8; 139ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The offset is immediately before a variation selector and a ZWJ that were seen before a 140ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // ZWJ emoji. 1414d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_BEFORE_VS_AND_ZWJ = 9; 142ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 143ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The number of following RIS code points is odd. 1444d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_ODD_NUMBERED_RIS = 10; 145ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The number of following RIS code points is even. 1464d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka final int STATE_EVEN_NUMBERED_RIS = 11; 147ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 148d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka // The offset is in emoji tag sequence. 149d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka final int STATE_IN_TAG_SEQUENCE = 12; 150d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka 151ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // The state machine has been stopped. 152d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka final int STATE_FINISHED = 13; 153ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 154ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int deleteCharCount = 0; // Char count to be deleted by backspace. 155ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int lastSeenVSCharCount = 0; // Char count of previous variation selector. 156ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 157ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int state = STATE_START; 158ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 159ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int tmpOffset = offset; 160ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka do { 161ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int codePoint = Character.codePointBefore(text, tmpOffset); 162ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka tmpOffset -= Character.charCount(codePoint); 163ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 164ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka switch (state) { 165ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_START: 166ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount = Character.charCount(codePoint); 1674d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka if (codePoint == LINE_FEED) { 1684d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka state = STATE_LF; 1694d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka } else if (isVariationSelector(codePoint)) { 170ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_VS; 171ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (Emoji.isRegionalIndicatorSymbol(codePoint)) { 172ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_ODD_NUMBERED_RIS; 173ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (Emoji.isEmojiModifier(codePoint)) { 174ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_EMOJI_MODIFIER; 175ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (codePoint == Emoji.COMBINING_ENCLOSING_KEYCAP) { 176ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_KEYCAP; 1773675d3cd84c446d05b3e3e97b04cf0427b2f5da6Seigo Nonaka } else if (Emoji.isEmoji(codePoint)) { 178bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka state = STATE_BEFORE_EMOJI; 179d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka } else if (codePoint == Emoji.CANCEL_TAG) { 180d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka state = STATE_IN_TAG_SEQUENCE; 181ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 182ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 183ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 184ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 1854d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka case STATE_LF: 1864d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka if (codePoint == CARRIAGE_RETURN) { 1874d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka ++deleteCharCount; 1884d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka } 1894d19160ef18779502cf0559f9cc0db184f7fbc21Seigo Nonaka state = STATE_FINISHED; 19033fac38ba9b02e9de05a53f00a708d7d7b47d12bSeigo Nonaka break; 191ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_ODD_NUMBERED_RIS: 192ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (Emoji.isRegionalIndicatorSymbol(codePoint)) { 193ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += 2; /* Char count of RIS */ 194ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_EVEN_NUMBERED_RIS; 195ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 196ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 197ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 198ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 199ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_EVEN_NUMBERED_RIS: 200ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (Emoji.isRegionalIndicatorSymbol(codePoint)) { 201ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount -= 2; /* Char count of RIS */ 202ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_ODD_NUMBERED_RIS; 203ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 204ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 205ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 206ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 207ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_KEYCAP: 208ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (isVariationSelector(codePoint)) { 209ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka lastSeenVSCharCount = Character.charCount(codePoint); 210ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_VS_AND_KEYCAP; 211ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 212ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 213ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 214ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (Emoji.isKeycapBase(codePoint)) { 215ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += Character.charCount(codePoint); 216ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 217ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 218ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 219ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_VS_AND_KEYCAP: 220ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (Emoji.isKeycapBase(codePoint)) { 221ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += lastSeenVSCharCount + Character.charCount(codePoint); 222ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 223ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 224ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 225ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_EMOJI_MODIFIER: 226ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (isVariationSelector(codePoint)) { 227ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka lastSeenVSCharCount = Character.charCount(codePoint); 228ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_VS_AND_EMOJI_MODIFIER; 229ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 230ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (Emoji.isEmojiModifierBase(codePoint)) { 231ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += Character.charCount(codePoint); 232ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 233ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 234ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 235ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_VS_AND_EMOJI_MODIFIER: 236ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (Emoji.isEmojiModifierBase(codePoint)) { 237ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += lastSeenVSCharCount + Character.charCount(codePoint); 238ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 239ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 240ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 241ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_VS: 2423675d3cd84c446d05b3e3e97b04cf0427b2f5da6Seigo Nonaka if (Emoji.isEmoji(codePoint)) { 243ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += Character.charCount(codePoint); 244bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka state = STATE_BEFORE_EMOJI; 245ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 246ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 247ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 248ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (!isVariationSelector(codePoint) && 249ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka UCharacter.getCombiningClass(codePoint) == 0) { 250ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += Character.charCount(codePoint); 251ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 252ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 253ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 254bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka case STATE_BEFORE_EMOJI: 255ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (codePoint == Emoji.ZERO_WIDTH_JOINER) { 256ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_ZWJ; 257ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 258ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 259ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 260ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 261ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_ZWJ: 2623675d3cd84c446d05b3e3e97b04cf0427b2f5da6Seigo Nonaka if (Emoji.isEmoji(codePoint)) { 263ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += Character.charCount(codePoint) + 1; // +1 for ZWJ. 264bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka state = Emoji.isEmojiModifier(codePoint) ? 265bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka STATE_BEFORE_EMOJI_MODIFIER : STATE_BEFORE_EMOJI; 266ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (isVariationSelector(codePoint)) { 267ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka lastSeenVSCharCount = Character.charCount(codePoint); 268ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_BEFORE_VS_AND_ZWJ; 269ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 270ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 271ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 272ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 273ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka case STATE_BEFORE_VS_AND_ZWJ: 2743675d3cd84c446d05b3e3e97b04cf0427b2f5da6Seigo Nonaka if (Emoji.isEmoji(codePoint)) { 275ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // +1 for ZWJ. 276ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka deleteCharCount += lastSeenVSCharCount + 1 + Character.charCount(codePoint); 277ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka lastSeenVSCharCount = 0; 278bba8d97c369f02a9d1988217324724a24842079fSeigo Nonaka state = STATE_BEFORE_EMOJI; 279ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 280ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka state = STATE_FINISHED; 281ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 282ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka break; 283d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka case STATE_IN_TAG_SEQUENCE: 284d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka if (Emoji.isTagSpecChar(codePoint)) { 285d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka deleteCharCount += 2; /* Char count of emoji tag spec character. */ 286d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka // Keep the same state. 287d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka } else if (Emoji.isEmoji(codePoint)) { 288d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka deleteCharCount += Character.charCount(codePoint); 289d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka state = STATE_FINISHED; 290d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka } else { 291d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka // Couldn't find tag_base character. Delete the last tag_term character. 292d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka deleteCharCount = 2; // for U+E007F 293d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka state = STATE_FINISHED; 294d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka } 295d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka // TODO: Need handle emoji variation selectors. Issue 35224297 296d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka break; 297ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka default: 298ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException("state " + state + " is unknown"); 299ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 300ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } while (tmpOffset > 0 && state != STATE_FINISHED); 301ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 302ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return adjustReplacementSpan(text, offset - deleteCharCount, true /* move to the start */); 303ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 304ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 305ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Returns the end offset to be deleted by a forward delete key from the given offset. 30623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka private static int getOffsetForForwardDeleteKey(CharSequence text, int offset, Paint paint) { 307ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int len = text.length(); 308ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 309ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (offset >= len - 1) { 310ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return len; 311ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 312ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 31323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka offset = paint.getTextRunCursor(text, offset, len, Paint.DIRECTION_LTR /* not used */, 31423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka offset, Paint.CURSOR_AFTER); 315ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 316ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return adjustReplacementSpan(text, offset, false /* move to the end */); 317ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 318ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 319d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode, 320d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown KeyEvent event, boolean isForwardDelete) { 321f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // Ensure the key event does not have modifiers except ALT or SHIFT or CTRL. 322d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState() 323f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK))) { 3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 327d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown // If there is a current selection, delete it. 32814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (deleteSelection(view, content)) { 32914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 332f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // MetaKeyKeyListener doesn't track control key state. Need to check the KeyEvent instead. 333f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka boolean isCtrlActive = ((event.getMetaState() & KeyEvent.META_CTRL_ON) != 0); 334f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka boolean isShiftActive = (getMetaState(content, META_SHIFT_ON, event) == 1); 335f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka boolean isAltActive = (getMetaState(content, META_ALT_ON, event) == 1); 336f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 337f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (isCtrlActive) { 338f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (isAltActive || isShiftActive) { 339f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // Ctrl+Alt, Ctrl+Shift, Ctrl+Alt+Shift should not delete any characters. 340f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka return false; 341d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } 342f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka return deleteUntilWordBoundary(view, content, isForwardDelete); 343f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 344f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 345f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. 346f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (isAltActive && deleteLine(view, content)) { 347f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka return true; 34814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 350d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown // Delete a character. 35114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int start = Selection.getSelectionEnd(content); 352d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown final int end; 353375f3153a93b802cf9edf824f30b230c42bb3e41Michael Wright if (isForwardDelete) { 35423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka final Paint paint; 35523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka if (view instanceof TextView) { 35623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka paint = ((TextView)view).getPaint(); 35723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka } else { 35823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka synchronized (mLock) { 35923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka if (sCachedPaint == null) { 36023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka sCachedPaint = new Paint(); 36123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka } 36223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka paint = sCachedPaint; 36323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka } 36423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka } 36523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka end = getOffsetForForwardDeleteKey(content, start, paint); 366d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } else { 367ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka end = getOffsetForBackspaceKey(content, start); 368d17247164b3c4e8a41cbe1dd283ef2442ad76229Jeff Brown } 36914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (start != end) { 37014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.delete(Math.min(start, end), Math.max(start, end)); 37114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 37314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return false; 37414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 376f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka private boolean deleteUntilWordBoundary(View view, Editable content, boolean isForwardDelete) { 377f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka int currentCursorOffset = Selection.getSelectionStart(content); 378f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 379f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // If there is a selection, do nothing. 380f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (currentCursorOffset != Selection.getSelectionEnd(content)) { 381f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka return false; 382f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 383f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 384f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // Early exit if there is no contents to delete. 385f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if ((!isForwardDelete && currentCursorOffset == 0) || 386f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka (isForwardDelete && currentCursorOffset == content.length())) { 387f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka return false; 388f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 389f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 390f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka WordIterator wordIterator = null; 391f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (view instanceof TextView) { 392f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka wordIterator = ((TextView)view).getWordIterator(); 393f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 394f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 395f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (wordIterator == null) { 396f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // Default locale is used for WordIterator since the appropriate locale is not clear 397f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // here. 398f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka // TODO: Use appropriate locale for WordIterator. 399f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka wordIterator = new WordIterator(); 400f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 401f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 402f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka int deleteFrom; 403f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka int deleteTo; 404f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 405f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (isForwardDelete) { 406f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka deleteFrom = currentCursorOffset; 407f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka wordIterator.setCharSequence(content, deleteFrom, content.length()); 408f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka deleteTo = wordIterator.following(currentCursorOffset); 409f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (deleteTo == BreakIterator.DONE) { 410f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka deleteTo = content.length(); 411f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 412f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } else { 413f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka deleteTo = currentCursorOffset; 414f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka wordIterator.setCharSequence(content, 0, deleteTo); 415f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka deleteFrom = wordIterator.preceding(currentCursorOffset); 416f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka if (deleteFrom == BreakIterator.DONE) { 417f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka deleteFrom = 0; 418f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 419f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 420f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka content.delete(deleteFrom, deleteTo); 421f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka return true; 422f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka } 423f2b233d44c796ceab3048a0130682fb5da3db557Seigo Nonaka 42414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown private boolean deleteSelection(View view, Editable content) { 42514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionStart = Selection.getSelectionStart(content); 42614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionEnd = Selection.getSelectionEnd(content); 42714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (selectionEnd < selectionStart) { 42814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int temp = selectionEnd; 42914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionEnd = selectionStart; 43014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionStart = temp; 43114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 43214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (selectionStart != selectionEnd) { 43314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.delete(selectionStart, selectionEnd); 43414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 43614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return false; 43714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 43914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown private boolean deleteLine(View view, Editable content) { 44014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (view instanceof TextView) { 44114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final Layout layout = ((TextView) view).getLayout(); 44214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (layout != null) { 44314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int line = layout.getLineForOffset(Selection.getSelectionStart(content)); 44414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int start = layout.getLineStart(line); 44514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown final int end = layout.getLineEnd(line); 44614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (end != start) { 44714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.delete(start, end); 44814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return true; 44914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 45014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 45114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 45214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown return false; 4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static int makeTextContentType(Capitalize caps, boolean autoText) { 4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int contentType = InputType.TYPE_CLASS_TEXT; 4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (caps) { 4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case CHARACTERS: 4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; 4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case WORDS: 4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; 4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case SENTENCES: 4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; 4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (autoText) { 4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project contentType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; 4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return contentType; 4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 47314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyDown(View view, Editable content, 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int keyCode, KeyEvent event) { 47614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown boolean handled; 47714d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown switch (keyCode) { 47814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown case KeyEvent.KEYCODE_DEL: 47914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown handled = backspace(view, content, keyCode, event); 48014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown break; 48114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown case KeyEvent.KEYCODE_FORWARD_DEL: 48214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown handled = forwardDelete(view, content, keyCode, event); 48314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown break; 48414d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown default: 48514d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown handled = false; 48614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown break; 4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 48814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 48914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (handled) { 49014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown adjustMetaAfterKeypress(content); 491b23ce29b8f2dd243b006a5211fe4b9bfc22ba08cBen Kwa return true; 49214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown } 49314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return super.onKeyDown(view, content, keyCode, event); 4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 49614d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting 4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the event's text into the content. 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyOther(View view, Editable content, KeyEvent event) { 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (event.getAction() != KeyEvent.ACTION_MULTIPLE 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project || event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) { 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Not something we are interested in. 5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 50814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionStart = Selection.getSelectionStart(content); 50914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int selectionEnd = Selection.getSelectionEnd(content); 51014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown if (selectionEnd < selectionStart) { 51114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown int temp = selectionEnd; 51214d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionEnd = selectionStart; 51314d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown selectionStart = temp; 5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project CharSequence text = event.getCharacters(); 5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (text == null) { 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 52014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown 52114d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown content.replace(selectionStart, selectionEnd, text); 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 525