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