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
194037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.AutoText;
204037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Editable;
214037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.NoCopySpan;
224037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Selection;
234037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.Spannable;
244037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.text.TextUtils;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener.Capitalize;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.SparseArray;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyCharacterMap;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the standard key listener for alphabetic input on qwerty
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * keyboards.  You should generally not need to instantiate this yourself;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * TextKeyListener will do it for you.
35405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * <p></p>
36405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * As for all implementations of {@link KeyListener}, this class is only concerned
37405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * with hardware keyboards.  Software input methods have no obligation to trigger
38405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * the methods in this class.
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class QwertyKeyListener extends BaseKeyListener {
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static QwertyKeyListener[] sInstance =
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new QwertyKeyListener[Capitalize.values().length * 2];
4347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private static QwertyKeyListener sFullKeyboardInstance;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private Capitalize mAutoCap;
4647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private boolean mAutoText;
4747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private boolean mFullKeyboard;
4847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown
4947e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private QwertyKeyListener(Capitalize cap, boolean autoText, boolean fullKeyboard) {
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAutoCap = cap;
5147e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        mAutoText = autoText;
5247e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        mFullKeyboard = fullKeyboard;
5347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    }
5447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown
5547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    public QwertyKeyListener(Capitalize cap, boolean autoText) {
5647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        this(cap, autoText, false);
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a new or existing instance with the specified capitalization
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and correction properties.
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    public static QwertyKeyListener getInstance(boolean autoText, Capitalize cap) {
6447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        int off = cap.ordinal() * 2 + (autoText ? 1 : 0);
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sInstance[off] == null) {
6747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            sInstance[off] = new QwertyKeyListener(cap, autoText);
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sInstance[off];
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    /**
7447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     * Gets an instance of the listener suitable for use with full keyboards.
7547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     * Disables auto-capitalization, auto-text and long-press initiated on-screen
7647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     * character pickers.
7747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     */
7847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    public static QwertyKeyListener getInstanceForFullKeyboard() {
7947e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        if (sFullKeyboardInstance == null) {
8047e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            sFullKeyboardInstance = new QwertyKeyListener(Capitalize.NONE, false, true);
8147e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        }
8247e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        return sFullKeyboardInstance;
8347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    }
8447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getInputType() {
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return makeTextContentType(mAutoCap, mAutoText);
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
884037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onKeyDown(View view, Editable content,
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             int keyCode, KeyEvent event) {
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int selStart, selEnd;
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int pref = 0;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            pref = TextKeyListener.getInstance().getPrefs(view.getContext());
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int a = Selection.getSelectionStart(content);
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int b = Selection.getSelectionEnd(content);
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selStart = Math.min(a, b);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selEnd = Math.max(a, b);
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart < 0 || selEnd < 0) {
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selStart = selEnd = 0;
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(content, 0, 0);
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int activeStart = content.getSpanStart(TextKeyListener.ACTIVE);
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int activeEnd = content.getSpanEnd(TextKeyListener.ACTIVE);
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // QWERTY keyboard normal case
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11614f10e5d5f51bc54ca2a45ee62d3cfb6debd3af0Raph Levien        int i = event.getUnicodeChar(getMetaState(content, event));
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        if (!mFullKeyboard) {
11947e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            int count = event.getRepeatCount();
12047e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            if (count > 0 && selStart == selEnd && selStart > 0) {
12147e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                char c = content.charAt(selStart - 1);
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
123e004b868f3e10d133798cfc719a69e3a1db0fa18Dmitry Tsyganyuk                if ((c == i || c == Character.toUpperCase(i)) && view != null) {
12447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                    if (showCharacterPicker(view, content, c, false, count)) {
12547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                        resetMetaState(content);
12647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                        return true;
12747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                    }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i == KeyCharacterMap.PICKER_DIALOG_INPUT) {
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view != null) {
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                showCharacterPicker(view, content,
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    KeyCharacterMap.PICKER_DIALOG_INPUT, true, 1);
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            resetMetaState(content);
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i == KeyCharacterMap.HEX_INPUT) {
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart == selEnd) {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                start = selEnd;
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (start > 0 && selEnd - start < 4 &&
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                       Character.digit(content.charAt(start - 1), 16) >= 0) {
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    start--;
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                start = selStart;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ch = -1;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String hex = TextUtils.substring(content, start, selEnd);
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ch = Integer.parseInt(hex, 16);
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException nfe) { }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch >= 0) {
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selStart = start;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(content, selStart, selEnd);
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = ch;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = 0;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i != 0) {
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean dead = false;
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((i & KeyCharacterMap.COMBINING_ACCENT) != 0) {
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dead = true;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = i & KeyCharacterMap.COMBINING_ACCENT_MASK;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (activeStart == selStart && activeEnd == selEnd) {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean replace = false;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (selEnd - selStart - 1 == 0) {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char accent = content.charAt(selStart);
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int composed = event.getDeadChar(accent, i);
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (composed != 0) {
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i = composed;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        replace = true;
188548655045a9c9791bd33e77a823afea5846228abJean Chalard                        dead = false;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!replace) {
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Selection.setSelection(content, selEnd);
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.removeSpan(TextKeyListener.ACTIVE);
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    selStart = selEnd;
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1994037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas            if ((pref & TextKeyListener.AUTO_CAP) != 0
2004037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas                    && Character.isLowerCase(i)
2014037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas                    && TextKeyListener.shouldCap(mAutoCap, content, selStart)) {
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int where = content.getSpanEnd(TextKeyListener.CAPPED);
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int flags = content.getSpanFlags(TextKeyListener.CAPPED);
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (where == selStart && (((flags >> 16) & 0xFFFF) == i)) {
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.removeSpan(TextKeyListener.CAPPED);
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    flags = i << 16;
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i = Character.toUpperCase(i);
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (selStart == 0)
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.setSpan(TextKeyListener.CAPPED, 0, 0,
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        Spannable.SPAN_MARK_MARK | flags);
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    else
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.setSpan(TextKeyListener.CAPPED,
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        selStart - 1, selStart,
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        flags);
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart != selEnd) {
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(content, selEnd);
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            content.setSpan(OLD_SEL_START, selStart, selStart,
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            Spannable.SPAN_MARK_MARK);
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            content.replace(selStart, selEnd, String.valueOf((char) i));
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int oldStart = content.getSpanStart(OLD_SEL_START);
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selEnd = Selection.getSelectionEnd(content);
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldStart < selEnd) {
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                content.setSpan(TextKeyListener.LAST_TYPED,
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                oldStart, selEnd,
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dead) {
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Selection.setSelection(content, oldStart, selEnd);
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.setSpan(TextKeyListener.ACTIVE, oldStart, selEnd,
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            adjustMetaAfterKeypress(content);
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // potentially do autotext replacement if the character
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // that was typed was an autotext terminator
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((pref & TextKeyListener.AUTO_TEXT) != 0 && mAutoText &&
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                (i == ' ' || i == '\t' || i == '\n' ||
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 i == ',' || i == '.' || i == '!' || i == '?' ||
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 i == '"' || Character.getType(i) == Character.END_PUNCTUATION) &&
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 content.getSpanEnd(TextKeyListener.INHIBIT_REPLACEMENT)
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     != oldStart) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int x;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (x = oldStart; x > 0; x--) {
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char c = content.charAt(x - 1);
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c != '\'' && !Character.isLetter(c)) {
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String rep = getReplacement(content, x, oldStart, view);
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (rep != null) {
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Replaced[] repl = content.getSpans(0, content.length(),
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     Replaced.class);
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int a = 0; a < repl.length; a++)
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.removeSpan(repl[a]);
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char[] orig = new char[oldStart - x];
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    TextUtils.getChars(content, x, oldStart, orig, 0);
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.setSpan(new Replaced(orig), x, oldStart,
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.replace(x, oldStart, rep);
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Replace two spaces by a period and a space.
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((pref & TextKeyListener.AUTO_PERIOD) != 0 && mAutoText) {
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selEnd = Selection.getSelectionEnd(content);
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (selEnd - 3 >= 0) {
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (content.charAt(selEnd - 1) == ' ' &&
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.charAt(selEnd - 2) == ' ') {
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        char c = content.charAt(selEnd - 3);
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        for (int j = selEnd - 3; j > 0; j--) {
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (c == '"' ||
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Character.getType(c) == Character.END_PUNCTUATION) {
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                c = content.charAt(j - 1);
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            } else {
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                break;
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (Character.isLetter(c) || Character.isDigit(c)) {
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            content.replace(selEnd - 2, selEnd - 1, ".");
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
30814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown        } else if (keyCode == KeyEvent.KEYCODE_DEL
30914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown                && (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_ALT_ON))
31014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown                && selStart == selEnd) {
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // special backspace case for undoing autotext
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int consider = 1;
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // if backspacing over the last typed character,
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // it undoes the autotext prior to that character
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // (unless the character typed was newline, in which
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // case this behavior would be confusing)
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (content.getSpanEnd(TextKeyListener.LAST_TYPED) == selStart) {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (content.charAt(selStart - 1) != '\n')
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    consider = 2;
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Replaced[] repl = content.getSpans(selStart - consider, selStart,
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                             Replaced.class);
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (repl.length > 0) {
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int st = content.getSpanStart(repl[0]);
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int en = content.getSpanEnd(repl[0]);
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String old = new String(repl[0].mText);
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                content.removeSpan(repl[0]);
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3354df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                // only cancel the autocomplete if the cursor is at the end of
336c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // the replaced span (or after it, because the user is
337c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // backspacing over the space after the word, not the word
338c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // itself).
339c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                if (selStart >= en) {
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT,
3414df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                                    en, en, Spannable.SPAN_POINT_POINT);
3424df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    content.replace(st, en, old);
3434df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project
3444df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    en = content.getSpanStart(TextKeyListener.INHIBIT_REPLACEMENT);
3454df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    if (en - 1 >= 0) {
3464df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                        content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT,
3474df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                                        en - 1, en,
3484df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
3494df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    } else {
3504df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                        content.removeSpan(TextKeyListener.INHIBIT_REPLACEMENT);
3514df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    }
3524df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    adjustMetaAfterKeypress(content);
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3544df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    adjustMetaAfterKeypress(content);
3554df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    return super.onKeyDown(view, content, keyCode, event);
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.onKeyDown(view, content, keyCode, event);
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String getReplacement(CharSequence src, int start, int end,
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  View view) {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = end - start;
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean changecase = false;
3694037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String replacement = AutoText.get(src, start, end, view);
3714037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (replacement == null) {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String key = TextUtils.substring(src, start, end).toLowerCase();
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            replacement = AutoText.get(key, 0, end - start, view);
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            changecase = true;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (replacement == null)
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3804037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int caps = 0;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (changecase) {
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = start; j < end; j++) {
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Character.isUpperCase(src.charAt(j)))
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    caps++;
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String out;
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (caps == 0)
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = replacement;
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (caps == 1)
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = toTitleCase(replacement);
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (caps == len)
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = replacement.toUpperCase();
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = toTitleCase(replacement);
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (out.length() == len &&
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.regionMatches(src, start, out, 0, len))
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return out;
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Marks the specified region of <code>content</code> as having
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * contained <code>original</code> prior to AutoText replacement.
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Call this method when you have done or are about to do an
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * AutoText-style replacement on a region of text and want to let
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the same mechanism (the user pressing DEL immediately after the
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * change) undo the replacement.
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param content the Editable text where the replacement was made
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param start the start of the replaced region
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param end the end of the replaced region; the location of the cursor
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param original the text to be restored if the user presses DEL
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void markAsReplaced(Spannable content, int start, int end,
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                      String original) {
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Replaced[] repl = content.getSpans(0, content.length(), Replaced.class);
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int a = 0; a < repl.length; a++) {
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            content.removeSpan(repl[a]);
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = original.length();
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] orig = new char[len];
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        original.getChars(0, len, orig, 0);
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        content.setSpan(new Replaced(orig), start, end,
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static SparseArray<String> PICKER_SETS =
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new SparseArray<String>();
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static {
439a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('A', "\u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5\u0104\u0100");
440a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('C', "\u00C7\u0106\u010C");
441a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('D', "\u010E");
442a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('E', "\u00C8\u00C9\u00CA\u00CB\u0118\u011A\u0112");
443ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('G', "\u011E");
444a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('L', "\u0141");
4454ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('I', "\u00CC\u00CD\u00CE\u00CF\u012A\u0130");
446a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('N', "\u00D1\u0143\u0147");
447a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('O', "\u00D8\u0152\u00D5\u00D2\u00D3\u00D4\u00D6\u014C");
448a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('R', "\u0158");
449ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('S', "\u015A\u0160\u015E");
450a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('T', "\u0164");
451a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('U', "\u00D9\u00DA\u00DB\u00DC\u016E\u016A");
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PICKER_SETS.put('Y', "\u00DD\u0178");
453a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('Z', "\u0179\u017B\u017D");
454a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('a', "\u00E0\u00E1\u00E2\u00E4\u00E6\u00E3\u00E5\u0105\u0101");
455a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('c', "\u00E7\u0107\u010D");
456a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('d', "\u010F");
457a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('e', "\u00E8\u00E9\u00EA\u00EB\u0119\u011B\u0113");
458ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('g', "\u011F");
4594ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('i', "\u00EC\u00ED\u00EE\u00EF\u012B\u0131");
460a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('l', "\u0142");
461a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('n', "\u00F1\u0144\u0148");
462a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('o', "\u00F8\u0153\u00F5\u00F2\u00F3\u00F4\u00F6\u014D");
463a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('r', "\u0159");
464ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('s', "\u00A7\u00DF\u015B\u0161\u015F");
465a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('t', "\u0165");
466a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('u', "\u00F9\u00FA\u00FB\u00FC\u016F\u016B");
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PICKER_SETS.put('y', "\u00FD\u00FF");
468a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('z', "\u017A\u017C\u017E");
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT,
470d7c432bf7f9c0c9dd6d954fffcd68828c0e309a5Eric Fischer                             "\u2026\u00A5\u2022\u00AE\u00A9\u00B1[]{}\\|");
471cebe347e7ff99c593a0136ae8bcb3cadab7d77b6Eric Fischer        PICKER_SETS.put('/', "\\");
4724ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4734ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        // From packages/inputmethods/LatinIME/res/xml/kbd_symbols.xml
4744ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4754ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('1', "\u00b9\u00bd\u2153\u00bc\u215b");
4764ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('2', "\u00b2\u2154");
4774ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('3', "\u00b3\u00be\u215c");
4784ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('4', "\u2074");
4794ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('5', "\u215d");
4804ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('7', "\u215e");
4814ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('0', "\u207f\u2205");
4824ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('$', "\u00a2\u00a3\u20ac\u00a5\u20a3\u20a4\u20b1");
4834ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('%', "\u2030");
4844ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('*', "\u2020\u2021");
4854ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('-', "\u2013\u2014");
4864ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('+', "\u00b1");
4874ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('(', "[{<");
4884ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put(')', "]}>");
4894ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('!', "\u00a1");
4904ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('"', "\u201c\u201d\u00ab\u00bb\u02dd");
4914ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('?', "\u00bf");
4924ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put(',', "\u201a\u201e");
4934ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4944ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        // From packages/inputmethods/LatinIME/res/xml/kbd_symbols_shift.xml
4954ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4964ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('=', "\u2260\u2248\u221e");
4974ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('<', "\u2264\u00ab\u2039");
4984ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('>', "\u2265\u00bb\u203a");
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean showCharacterPicker(View view, Editable content, char c,
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        boolean insert, int count) {
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String set = PICKER_SETS.get(c);
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (set == null) {
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == 1) {
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            new CharacterPickerDialog(view.getContext(),
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                      view, content, set, insert).show();
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String toTitleCase(String src) {
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Character.toUpperCase(src.charAt(0)) + src.substring(1);
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static class Replaced implements NoCopySpan
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Replaced(char[] text) {
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mText = text;
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private char[] mText;
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
530