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