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
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.*;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener.Capitalize;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.SparseArray;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyCharacterMap;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is the standard key listener for alphabetic input on qwerty
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * keyboards.  You should generally not need to instantiate this yourself;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * TextKeyListener will do it for you.
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class QwertyKeyListener extends BaseKeyListener {
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static QwertyKeyListener[] sInstance =
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new QwertyKeyListener[Capitalize.values().length * 2];
3447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private static QwertyKeyListener sFullKeyboardInstance;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private Capitalize mAutoCap;
3747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private boolean mAutoText;
3847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private boolean mFullKeyboard;
3947e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown
4047e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    private QwertyKeyListener(Capitalize cap, boolean autoText, boolean fullKeyboard) {
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAutoCap = cap;
4247e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        mAutoText = autoText;
4347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        mFullKeyboard = fullKeyboard;
4447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    }
4547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown
4647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    public QwertyKeyListener(Capitalize cap, boolean autoText) {
4747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        this(cap, autoText, false);
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a new or existing instance with the specified capitalization
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and correction properties.
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    public static QwertyKeyListener getInstance(boolean autoText, Capitalize cap) {
5547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        int off = cap.ordinal() * 2 + (autoText ? 1 : 0);
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sInstance[off] == null) {
5847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            sInstance[off] = new QwertyKeyListener(cap, autoText);
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sInstance[off];
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    /**
6547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     * Gets an instance of the listener suitable for use with full keyboards.
6647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     * Disables auto-capitalization, auto-text and long-press initiated on-screen
6747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     * character pickers.
6847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown     */
6947e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    public static QwertyKeyListener getInstanceForFullKeyboard() {
7047e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        if (sFullKeyboardInstance == null) {
7147e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            sFullKeyboardInstance = new QwertyKeyListener(Capitalize.NONE, false, true);
7247e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        }
7347e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        return sFullKeyboardInstance;
7447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown    }
7547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getInputType() {
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return makeTextContentType(mAutoCap, mAutoText);
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onKeyDown(View view, Editable content,
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             int keyCode, KeyEvent event) {
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int selStart, selEnd;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int pref = 0;
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            pref = TextKeyListener.getInstance().getPrefs(view.getContext());
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int a = Selection.getSelectionStart(content);
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int b = Selection.getSelectionEnd(content);
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selStart = Math.min(a, b);
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selEnd = Math.max(a, b);
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart < 0 || selEnd < 0) {
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selStart = selEnd = 0;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(content, 0, 0);
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int activeStart = content.getSpanStart(TextKeyListener.ACTIVE);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int activeEnd = content.getSpanEnd(TextKeyListener.ACTIVE);
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // QWERTY keyboard normal case
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1076b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        int i = event.getUnicodeChar(event.getMetaState() | getMetaState(content));
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10947e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown        if (!mFullKeyboard) {
11047e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            int count = event.getRepeatCount();
11147e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown            if (count > 0 && selStart == selEnd && selStart > 0) {
11247e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                char c = content.charAt(selStart - 1);
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11447e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                if (c == i || c == Character.toUpperCase(i) && view != null) {
11547e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                    if (showCharacterPicker(view, content, c, false, count)) {
11647e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                        resetMetaState(content);
11747e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                        return true;
11847e6b1b5eef8ee99872f278f66bc498c4fcca0d8Jeff Brown                    }
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i == KeyCharacterMap.PICKER_DIALOG_INPUT) {
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view != null) {
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                showCharacterPicker(view, content,
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    KeyCharacterMap.PICKER_DIALOG_INPUT, true, 1);
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            resetMetaState(content);
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i == KeyCharacterMap.HEX_INPUT) {
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart == selEnd) {
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                start = selEnd;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (start > 0 && selEnd - start < 4 &&
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                       Character.digit(content.charAt(start - 1), 16) >= 0) {
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    start--;
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                start = selStart;
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ch = -1;
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String hex = TextUtils.substring(content, start, selEnd);
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ch = Integer.parseInt(hex, 16);
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException nfe) { }
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch >= 0) {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selStart = start;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(content, selStart, selEnd);
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = ch;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = 0;
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i != 0) {
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean dead = false;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((i & KeyCharacterMap.COMBINING_ACCENT) != 0) {
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dead = true;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = i & KeyCharacterMap.COMBINING_ACCENT_MASK;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (activeStart == selStart && activeEnd == selEnd) {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean replace = false;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (selEnd - selStart - 1 == 0) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char accent = content.charAt(selStart);
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int composed = event.getDeadChar(accent, i);
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (composed != 0) {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i = composed;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        replace = true;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!replace) {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Selection.setSelection(content, selEnd);
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.removeSpan(TextKeyListener.ACTIVE);
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    selStart = selEnd;
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((pref & TextKeyListener.AUTO_CAP) != 0 &&
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Character.isLowerCase(i) &&
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                TextKeyListener.shouldCap(mAutoCap, content, selStart)) {
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int where = content.getSpanEnd(TextKeyListener.CAPPED);
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int flags = content.getSpanFlags(TextKeyListener.CAPPED);
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (where == selStart && (((flags >> 16) & 0xFFFF) == i)) {
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.removeSpan(TextKeyListener.CAPPED);
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    flags = i << 16;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i = Character.toUpperCase(i);
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (selStart == 0)
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.setSpan(TextKeyListener.CAPPED, 0, 0,
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        Spannable.SPAN_MARK_MARK | flags);
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    else
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.setSpan(TextKeyListener.CAPPED,
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        selStart - 1, selStart,
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        flags);
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (selStart != selEnd) {
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Selection.setSelection(content, selEnd);
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            content.setSpan(OLD_SEL_START, selStart, selStart,
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            Spannable.SPAN_MARK_MARK);
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            content.replace(selStart, selEnd, String.valueOf((char) i));
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int oldStart = content.getSpanStart(OLD_SEL_START);
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            selEnd = Selection.getSelectionEnd(content);
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldStart < selEnd) {
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                content.setSpan(TextKeyListener.LAST_TYPED,
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                oldStart, selEnd,
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dead) {
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Selection.setSelection(content, oldStart, selEnd);
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.setSpan(TextKeyListener.ACTIVE, oldStart, selEnd,
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            adjustMetaAfterKeypress(content);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // potentially do autotext replacement if the character
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // that was typed was an autotext terminator
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((pref & TextKeyListener.AUTO_TEXT) != 0 && mAutoText &&
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                (i == ' ' || i == '\t' || i == '\n' ||
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 i == ',' || i == '.' || i == '!' || i == '?' ||
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 i == '"' || Character.getType(i) == Character.END_PUNCTUATION) &&
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 content.getSpanEnd(TextKeyListener.INHIBIT_REPLACEMENT)
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     != oldStart) {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int x;
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (x = oldStart; x > 0; x--) {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char c = content.charAt(x - 1);
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c != '\'' && !Character.isLetter(c)) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String rep = getReplacement(content, x, oldStart, view);
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (rep != null) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Replaced[] repl = content.getSpans(0, content.length(),
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                     Replaced.class);
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int a = 0; a < repl.length; a++)
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.removeSpan(repl[a]);
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char[] orig = new char[oldStart - x];
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    TextUtils.getChars(content, x, oldStart, orig, 0);
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.setSpan(new Replaced(orig), x, oldStart,
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.replace(x, oldStart, rep);
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Replace two spaces by a period and a space.
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((pref & TextKeyListener.AUTO_PERIOD) != 0 && mAutoText) {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                selEnd = Selection.getSelectionEnd(content);
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (selEnd - 3 >= 0) {
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (content.charAt(selEnd - 1) == ' ' &&
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        content.charAt(selEnd - 2) == ' ') {
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        char c = content.charAt(selEnd - 3);
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        for (int j = selEnd - 3; j > 0; j--) {
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (c == '"' ||
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Character.getType(c) == Character.END_PUNCTUATION) {
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                c = content.charAt(j - 1);
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            } else {
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                break;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (Character.isLetter(c) || Character.isDigit(c)) {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            content.replace(selEnd - 2, selEnd - 1, ".");
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
29814d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown        } else if (keyCode == KeyEvent.KEYCODE_DEL
29914d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown                && (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_ALT_ON))
30014d0ca1473b991288b2dfab57409054dec7cd2faJeff Brown                && selStart == selEnd) {
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // special backspace case for undoing autotext
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int consider = 1;
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // if backspacing over the last typed character,
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // it undoes the autotext prior to that character
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // (unless the character typed was newline, in which
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // case this behavior would be confusing)
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (content.getSpanEnd(TextKeyListener.LAST_TYPED) == selStart) {
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (content.charAt(selStart - 1) != '\n')
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    consider = 2;
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Replaced[] repl = content.getSpans(selStart - consider, selStart,
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                             Replaced.class);
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (repl.length > 0) {
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int st = content.getSpanStart(repl[0]);
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int en = content.getSpanEnd(repl[0]);
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String old = new String(repl[0].mText);
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                content.removeSpan(repl[0]);
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3254df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                // only cancel the autocomplete if the cursor is at the end of
326c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // the replaced span (or after it, because the user is
327c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // backspacing over the space after the word, not the word
328c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // itself).
329c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                if (selStart >= en) {
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT,
3314df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                                    en, en, Spannable.SPAN_POINT_POINT);
3324df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    content.replace(st, en, old);
3334df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project
3344df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    en = content.getSpanStart(TextKeyListener.INHIBIT_REPLACEMENT);
3354df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    if (en - 1 >= 0) {
3364df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                        content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT,
3374df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                                        en - 1, en,
3384df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
3394df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    } else {
3404df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                        content.removeSpan(TextKeyListener.INHIBIT_REPLACEMENT);
3414df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    }
3424df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    adjustMetaAfterKeypress(content);
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3444df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    adjustMetaAfterKeypress(content);
3454df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project                    return super.onKeyDown(view, content, keyCode, event);
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.onKeyDown(view, content, keyCode, event);
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String getReplacement(CharSequence src, int start, int end,
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  View view) {
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = end - start;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean changecase = false;
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String replacement = AutoText.get(src, start, end, view);
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (replacement == null) {
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String key = TextUtils.substring(src, start, end).toLowerCase();
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            replacement = AutoText.get(key, 0, end - start, view);
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            changecase = true;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (replacement == null)
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int caps = 0;
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (changecase) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = start; j < end; j++) {
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Character.isUpperCase(src.charAt(j)))
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    caps++;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String out;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (caps == 0)
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = replacement;
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (caps == 1)
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = toTitleCase(replacement);
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (caps == len)
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = replacement.toUpperCase();
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = toTitleCase(replacement);
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (out.length() == len &&
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.regionMatches(src, start, out, 0, len))
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return out;
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Marks the specified region of <code>content</code> as having
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * contained <code>original</code> prior to AutoText replacement.
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Call this method when you have done or are about to do an
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * AutoText-style replacement on a region of text and want to let
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the same mechanism (the user pressing DEL immediately after the
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * change) undo the replacement.
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param content the Editable text where the replacement was made
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param start the start of the replaced region
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param end the end of the replaced region; the location of the cursor
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param original the text to be restored if the user presses DEL
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void markAsReplaced(Spannable content, int start, int end,
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                      String original) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Replaced[] repl = content.getSpans(0, content.length(), Replaced.class);
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int a = 0; a < repl.length; a++) {
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            content.removeSpan(repl[a]);
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = original.length();
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] orig = new char[len];
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        original.getChars(0, len, orig, 0);
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        content.setSpan(new Replaced(orig), start, end,
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static SparseArray<String> PICKER_SETS =
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new SparseArray<String>();
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static {
429a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('A', "\u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5\u0104\u0100");
430a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('C', "\u00C7\u0106\u010C");
431a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('D', "\u010E");
432a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('E', "\u00C8\u00C9\u00CA\u00CB\u0118\u011A\u0112");
433ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('G', "\u011E");
434a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('L', "\u0141");
4354ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('I', "\u00CC\u00CD\u00CE\u00CF\u012A\u0130");
436a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('N', "\u00D1\u0143\u0147");
437a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('O', "\u00D8\u0152\u00D5\u00D2\u00D3\u00D4\u00D6\u014C");
438a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('R', "\u0158");
439ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('S', "\u015A\u0160\u015E");
440a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('T', "\u0164");
441a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('U', "\u00D9\u00DA\u00DB\u00DC\u016E\u016A");
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PICKER_SETS.put('Y', "\u00DD\u0178");
443a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('Z', "\u0179\u017B\u017D");
444a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('a', "\u00E0\u00E1\u00E2\u00E4\u00E6\u00E3\u00E5\u0105\u0101");
445a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('c', "\u00E7\u0107\u010D");
446a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('d', "\u010F");
447a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('e', "\u00E8\u00E9\u00EA\u00EB\u0119\u011B\u0113");
448ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('g', "\u011F");
4494ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('i', "\u00EC\u00ED\u00EE\u00EF\u012B\u0131");
450a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('l', "\u0142");
451a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('n', "\u00F1\u0144\u0148");
452a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('o', "\u00F8\u0153\u00F5\u00F2\u00F3\u00F4\u00F6\u014D");
453a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('r', "\u0159");
454ec1f8a2ecedb4904ca93bce14b4e1e033539fa55Eric Fischer        PICKER_SETS.put('s', "\u00A7\u00DF\u015B\u0161\u015F");
455a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('t', "\u0165");
456a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('u', "\u00F9\u00FA\u00FB\u00FC\u016F\u016B");
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PICKER_SETS.put('y', "\u00FD\u00FF");
458a3ea3ae07ff3fec565215ad73fc7430a07b57418Eric Fischer        PICKER_SETS.put('z', "\u017A\u017C\u017E");
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT,
460d7c432bf7f9c0c9dd6d954fffcd68828c0e309a5Eric Fischer                             "\u2026\u00A5\u2022\u00AE\u00A9\u00B1[]{}\\|");
461cebe347e7ff99c593a0136ae8bcb3cadab7d77b6Eric Fischer        PICKER_SETS.put('/', "\\");
4624ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4634ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        // From packages/inputmethods/LatinIME/res/xml/kbd_symbols.xml
4644ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4654ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('1', "\u00b9\u00bd\u2153\u00bc\u215b");
4664ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('2', "\u00b2\u2154");
4674ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('3', "\u00b3\u00be\u215c");
4684ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('4', "\u2074");
4694ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('5', "\u215d");
4704ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('7', "\u215e");
4714ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('0', "\u207f\u2205");
4724ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('$', "\u00a2\u00a3\u20ac\u00a5\u20a3\u20a4\u20b1");
4734ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('%', "\u2030");
4744ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('*', "\u2020\u2021");
4754ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('-', "\u2013\u2014");
4764ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('+', "\u00b1");
4774ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('(', "[{<");
4784ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put(')', "]}>");
4794ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('!', "\u00a1");
4804ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('"', "\u201c\u201d\u00ab\u00bb\u02dd");
4814ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('?', "\u00bf");
4824ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put(',', "\u201a\u201e");
4834ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4844ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        // From packages/inputmethods/LatinIME/res/xml/kbd_symbols_shift.xml
4854ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer
4864ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('=', "\u2260\u2248\u221e");
4874ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('<', "\u2264\u00ab\u2039");
4884ef29959bda59ae5f90aeeb48a8ef7e218500d03Eric Fischer        PICKER_SETS.put('>', "\u2265\u00bb\u203a");
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean showCharacterPicker(View view, Editable content, char c,
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        boolean insert, int count) {
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String set = PICKER_SETS.get(c);
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (set == null) {
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (count == 1) {
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            new CharacterPickerDialog(view.getContext(),
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                      view, content, set, insert).show();
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String toTitleCase(String src) {
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Character.toUpperCase(src.charAt(0)) + src.substring(1);
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static class Replaced implements NoCopySpan
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Replaced(char[] text) {
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mText = text;
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private char[] mText;
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
520