FileDocCategorySizeDatePackage
QwertyKeyListener.javaAPI DocAndroid 5.1 API19670Thu Mar 12 22:22:10 GMT 2015android.text.method

QwertyKeyListener

public class QwertyKeyListener extends BaseKeyListener
This is the standard key listener for alphabetic input on qwerty keyboards. You should generally not need to instantiate this yourself; TextKeyListener will do it for you.

As for all implementations of {@link KeyListener}, this class is only concerned with hardware keyboards. Software input methods have no obligation to trigger the methods in this class.

Fields Summary
private static QwertyKeyListener[]
sInstance
private static QwertyKeyListener
sFullKeyboardInstance
private android.text.method.TextKeyListener.Capitalize
mAutoCap
private boolean
mAutoText
private boolean
mFullKeyboard
private static android.util.SparseArray
PICKER_SETS
Constructors Summary
private QwertyKeyListener(android.text.method.TextKeyListener.Capitalize cap, boolean autoText, boolean fullKeyboard)


           
        mAutoCap = cap;
        mAutoText = autoText;
        mFullKeyboard = fullKeyboard;
    
public QwertyKeyListener(android.text.method.TextKeyListener.Capitalize cap, boolean autoText)

        this(cap, autoText, false);
    
Methods Summary
public intgetInputType()

        return makeTextContentType(mAutoCap, mAutoText);
    
public static android.text.method.QwertyKeyListenergetInstance(boolean autoText, android.text.method.TextKeyListener.Capitalize cap)
Returns a new or existing instance with the specified capitalization and correction properties.

        int off = cap.ordinal() * 2 + (autoText ? 1 : 0);

        if (sInstance[off] == null) {
            sInstance[off] = new QwertyKeyListener(cap, autoText);
        }

        return sInstance[off];
    
public static android.text.method.QwertyKeyListenergetInstanceForFullKeyboard()
Gets an instance of the listener suitable for use with full keyboards. Disables auto-capitalization, auto-text and long-press initiated on-screen character pickers.

        if (sFullKeyboardInstance == null) {
            sFullKeyboardInstance = new QwertyKeyListener(Capitalize.NONE, false, true);
        }
        return sFullKeyboardInstance;
    
private java.lang.StringgetReplacement(java.lang.CharSequence src, int start, int end, android.view.View view)

        int len = end - start;
        boolean changecase = false;
        
        String replacement = AutoText.get(src, start, end, view);
        
        if (replacement == null) {
            String key = TextUtils.substring(src, start, end).toLowerCase();
            replacement = AutoText.get(key, 0, end - start, view);
            changecase = true;

            if (replacement == null)
                return null;
        }
        
        int caps = 0;

        if (changecase) {
            for (int j = start; j < end; j++) {
                if (Character.isUpperCase(src.charAt(j)))
                    caps++;
            }
        }

        String out;

        if (caps == 0)
            out = replacement;
        else if (caps == 1)
            out = toTitleCase(replacement);
        else if (caps == len)
            out = replacement.toUpperCase();
        else
            out = toTitleCase(replacement);

        if (out.length() == len &&
            TextUtils.regionMatches(src, start, out, 0, len))
            return null;

        return out;
    
public static voidmarkAsReplaced(Spannable content, int start, int end, java.lang.String original)
Marks the specified region of content as having contained original prior to AutoText replacement. Call this method when you have done or are about to do an AutoText-style replacement on a region of text and want to let the same mechanism (the user pressing DEL immediately after the change) undo the replacement.

param
content the Editable text where the replacement was made
param
start the start of the replaced region
param
end the end of the replaced region; the location of the cursor
param
original the text to be restored if the user presses DEL

        Replaced[] repl = content.getSpans(0, content.length(), Replaced.class);
        for (int a = 0; a < repl.length; a++) {
            content.removeSpan(repl[a]);
        }

        int len = original.length();
        char[] orig = new char[len];
        original.getChars(0, len, orig, 0);

        content.setSpan(new Replaced(orig), start, end,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    
public booleanonKeyDown(android.view.View view, Editable content, int keyCode, android.view.KeyEvent event)

        int selStart, selEnd;
        int pref = 0;

        if (view != null) {
            pref = TextKeyListener.getInstance().getPrefs(view.getContext());
        }

        {
            int a = Selection.getSelectionStart(content);
            int b = Selection.getSelectionEnd(content);

            selStart = Math.min(a, b);
            selEnd = Math.max(a, b);

            if (selStart < 0 || selEnd < 0) {
                selStart = selEnd = 0;
                Selection.setSelection(content, 0, 0);
            }
        }

        int activeStart = content.getSpanStart(TextKeyListener.ACTIVE);
        int activeEnd = content.getSpanEnd(TextKeyListener.ACTIVE);

        // QWERTY keyboard normal case

        int i = event.getUnicodeChar(getMetaState(content, event));

        if (!mFullKeyboard) {
            int count = event.getRepeatCount();
            if (count > 0 && selStart == selEnd && selStart > 0) {
                char c = content.charAt(selStart - 1);

                if ((c == i || c == Character.toUpperCase(i)) && view != null) {
                    if (showCharacterPicker(view, content, c, false, count)) {
                        resetMetaState(content);
                        return true;
                    }
                }
            }
        }

        if (i == KeyCharacterMap.PICKER_DIALOG_INPUT) {
            if (view != null) {
                showCharacterPicker(view, content,
                                    KeyCharacterMap.PICKER_DIALOG_INPUT, true, 1);
            }
            resetMetaState(content);
            return true;
        }

        if (i == KeyCharacterMap.HEX_INPUT) {
            int start;

            if (selStart == selEnd) {
                start = selEnd;

                while (start > 0 && selEnd - start < 4 &&
                       Character.digit(content.charAt(start - 1), 16) >= 0) {
                    start--;
                }
            } else {
                start = selStart;
            }

            int ch = -1;
            try {
                String hex = TextUtils.substring(content, start, selEnd);
                ch = Integer.parseInt(hex, 16);
            } catch (NumberFormatException nfe) { }

            if (ch >= 0) {
                selStart = start;
                Selection.setSelection(content, selStart, selEnd);
                i = ch;
            } else {
                i = 0;
            }
        }

        if (i != 0) {
            boolean dead = false;

            if ((i & KeyCharacterMap.COMBINING_ACCENT) != 0) {
                dead = true;
                i = i & KeyCharacterMap.COMBINING_ACCENT_MASK;
            }

            if (activeStart == selStart && activeEnd == selEnd) {
                boolean replace = false;

                if (selEnd - selStart - 1 == 0) {
                    char accent = content.charAt(selStart);
                    int composed = event.getDeadChar(accent, i);

                    if (composed != 0) {
                        i = composed;
                        replace = true;
                        dead = false;
                    }
                }

                if (!replace) {
                    Selection.setSelection(content, selEnd);
                    content.removeSpan(TextKeyListener.ACTIVE);
                    selStart = selEnd;
                }
            }

            if ((pref & TextKeyListener.AUTO_CAP) != 0 &&
                Character.isLowerCase(i) && 
                TextKeyListener.shouldCap(mAutoCap, content, selStart)) {
                int where = content.getSpanEnd(TextKeyListener.CAPPED);
                int flags = content.getSpanFlags(TextKeyListener.CAPPED);

                if (where == selStart && (((flags >> 16) & 0xFFFF) == i)) {
                    content.removeSpan(TextKeyListener.CAPPED);
                } else {
                    flags = i << 16;
                    i = Character.toUpperCase(i);

                    if (selStart == 0)
                        content.setSpan(TextKeyListener.CAPPED, 0, 0,
                                        Spannable.SPAN_MARK_MARK | flags);
                    else
                        content.setSpan(TextKeyListener.CAPPED,
                                        selStart - 1, selStart,
                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
                                        flags);
                }
            }

            if (selStart != selEnd) {
                Selection.setSelection(content, selEnd);
            }
            content.setSpan(OLD_SEL_START, selStart, selStart,
                            Spannable.SPAN_MARK_MARK);

            content.replace(selStart, selEnd, String.valueOf((char) i));

            int oldStart = content.getSpanStart(OLD_SEL_START);
            selEnd = Selection.getSelectionEnd(content);

            if (oldStart < selEnd) {
                content.setSpan(TextKeyListener.LAST_TYPED,
                                oldStart, selEnd,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

                if (dead) {
                    Selection.setSelection(content, oldStart, selEnd);
                    content.setSpan(TextKeyListener.ACTIVE, oldStart, selEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                }
            }

            adjustMetaAfterKeypress(content);

            // potentially do autotext replacement if the character
            // that was typed was an autotext terminator

            if ((pref & TextKeyListener.AUTO_TEXT) != 0 && mAutoText &&
                (i == ' " || i == '\t" || i == '\n" ||
                 i == '," || i == '." || i == '!" || i == '?" ||
                 i == '"" || Character.getType(i) == Character.END_PUNCTUATION) &&
                 content.getSpanEnd(TextKeyListener.INHIBIT_REPLACEMENT)
                     != oldStart) {
                int x;

                for (x = oldStart; x > 0; x--) {
                    char c = content.charAt(x - 1);
                    if (c != '\'" && !Character.isLetter(c)) {
                        break;
                    }
                }

                String rep = getReplacement(content, x, oldStart, view);

                if (rep != null) {
                    Replaced[] repl = content.getSpans(0, content.length(),
                                                     Replaced.class);
                    for (int a = 0; a < repl.length; a++)
                        content.removeSpan(repl[a]);

                    char[] orig = new char[oldStart - x];
                    TextUtils.getChars(content, x, oldStart, orig, 0);

                    content.setSpan(new Replaced(orig), x, oldStart,
                                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    content.replace(x, oldStart, rep);
                }
            }

            // Replace two spaces by a period and a space.

            if ((pref & TextKeyListener.AUTO_PERIOD) != 0 && mAutoText) {
                selEnd = Selection.getSelectionEnd(content);
                if (selEnd - 3 >= 0) {
                    if (content.charAt(selEnd - 1) == ' " &&
                        content.charAt(selEnd - 2) == ' ") {
                        char c = content.charAt(selEnd - 3);

                        for (int j = selEnd - 3; j > 0; j--) {
                            if (c == '"" ||
                                Character.getType(c) == Character.END_PUNCTUATION) {
                                c = content.charAt(j - 1);
                            } else {
                                break;
                            }
                        }

                        if (Character.isLetter(c) || Character.isDigit(c)) {
                            content.replace(selEnd - 2, selEnd - 1, ".");
                        }
                    }
                }
            }

            return true;
        } else if (keyCode == KeyEvent.KEYCODE_DEL
                && (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_ALT_ON))
                && selStart == selEnd) {
            // special backspace case for undoing autotext

            int consider = 1;

            // if backspacing over the last typed character,
            // it undoes the autotext prior to that character
            // (unless the character typed was newline, in which
            // case this behavior would be confusing)

            if (content.getSpanEnd(TextKeyListener.LAST_TYPED) == selStart) {
                if (content.charAt(selStart - 1) != '\n")
                    consider = 2;
            }

            Replaced[] repl = content.getSpans(selStart - consider, selStart,
                                             Replaced.class);

            if (repl.length > 0) {
                int st = content.getSpanStart(repl[0]);
                int en = content.getSpanEnd(repl[0]);
                String old = new String(repl[0].mText);

                content.removeSpan(repl[0]);

                // only cancel the autocomplete if the cursor is at the end of
                // the replaced span (or after it, because the user is
                // backspacing over the space after the word, not the word
                // itself).
                if (selStart >= en) {
                    content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT,
                                    en, en, Spannable.SPAN_POINT_POINT);
                    content.replace(st, en, old);

                    en = content.getSpanStart(TextKeyListener.INHIBIT_REPLACEMENT);
                    if (en - 1 >= 0) {
                        content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT,
                                        en - 1, en,
                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    } else {
                        content.removeSpan(TextKeyListener.INHIBIT_REPLACEMENT);
                    }
                    adjustMetaAfterKeypress(content);
                } else {
                    adjustMetaAfterKeypress(content);
                    return super.onKeyDown(view, content, keyCode, event);
                }

                return true;
            }
        }

        return super.onKeyDown(view, content, keyCode, event);
    
private booleanshowCharacterPicker(android.view.View view, Editable content, char c, boolean insert, int count)

     
        PICKER_SETS.put('A", "\u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5\u0104\u0100");
        PICKER_SETS.put('C", "\u00C7\u0106\u010C");
        PICKER_SETS.put('D", "\u010E");
        PICKER_SETS.put('E", "\u00C8\u00C9\u00CA\u00CB\u0118\u011A\u0112");
        PICKER_SETS.put('G", "\u011E");
        PICKER_SETS.put('L", "\u0141");
        PICKER_SETS.put('I", "\u00CC\u00CD\u00CE\u00CF\u012A\u0130");
        PICKER_SETS.put('N", "\u00D1\u0143\u0147");
        PICKER_SETS.put('O", "\u00D8\u0152\u00D5\u00D2\u00D3\u00D4\u00D6\u014C");
        PICKER_SETS.put('R", "\u0158");
        PICKER_SETS.put('S", "\u015A\u0160\u015E");
        PICKER_SETS.put('T", "\u0164");
        PICKER_SETS.put('U", "\u00D9\u00DA\u00DB\u00DC\u016E\u016A");
        PICKER_SETS.put('Y", "\u00DD\u0178");
        PICKER_SETS.put('Z", "\u0179\u017B\u017D");
        PICKER_SETS.put('a", "\u00E0\u00E1\u00E2\u00E4\u00E6\u00E3\u00E5\u0105\u0101");
        PICKER_SETS.put('c", "\u00E7\u0107\u010D");
        PICKER_SETS.put('d", "\u010F");
        PICKER_SETS.put('e", "\u00E8\u00E9\u00EA\u00EB\u0119\u011B\u0113");
        PICKER_SETS.put('g", "\u011F");
        PICKER_SETS.put('i", "\u00EC\u00ED\u00EE\u00EF\u012B\u0131");
        PICKER_SETS.put('l", "\u0142");
        PICKER_SETS.put('n", "\u00F1\u0144\u0148");
        PICKER_SETS.put('o", "\u00F8\u0153\u00F5\u00F2\u00F3\u00F4\u00F6\u014D");
        PICKER_SETS.put('r", "\u0159");
        PICKER_SETS.put('s", "\u00A7\u00DF\u015B\u0161\u015F");
        PICKER_SETS.put('t", "\u0165");
        PICKER_SETS.put('u", "\u00F9\u00FA\u00FB\u00FC\u016F\u016B");
        PICKER_SETS.put('y", "\u00FD\u00FF");
        PICKER_SETS.put('z", "\u017A\u017C\u017E");
        PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT,
                             "\u2026\u00A5\u2022\u00AE\u00A9\u00B1[]{}\\|");
        PICKER_SETS.put('/", "\\");

        // From packages/inputmethods/LatinIME/res/xml/kbd_symbols.xml

        PICKER_SETS.put('1", "\u00b9\u00bd\u2153\u00bc\u215b");
        PICKER_SETS.put('2", "\u00b2\u2154");
        PICKER_SETS.put('3", "\u00b3\u00be\u215c");
        PICKER_SETS.put('4", "\u2074");
        PICKER_SETS.put('5", "\u215d");
        PICKER_SETS.put('7", "\u215e");
        PICKER_SETS.put('0", "\u207f\u2205");
        PICKER_SETS.put('$", "\u00a2\u00a3\u20ac\u00a5\u20a3\u20a4\u20b1");
        PICKER_SETS.put('%", "\u2030");
        PICKER_SETS.put('*", "\u2020\u2021");
        PICKER_SETS.put('-", "\u2013\u2014");
        PICKER_SETS.put('+", "\u00b1");
        PICKER_SETS.put('(", "[{<");
        PICKER_SETS.put(')", "]}>");
        PICKER_SETS.put('!", "\u00a1");
        PICKER_SETS.put('"", "\u201c\u201d\u00ab\u00bb\u02dd");
        PICKER_SETS.put('?", "\u00bf");
        PICKER_SETS.put(',", "\u201a\u201e");

        // From packages/inputmethods/LatinIME/res/xml/kbd_symbols_shift.xml

        PICKER_SETS.put('=", "\u2260\u2248\u221e");
        PICKER_SETS.put('<", "\u2264\u00ab\u2039");
        PICKER_SETS.put('>", "\u2265\u00bb\u203a");
    
        String set = PICKER_SETS.get(c);
        if (set == null) {
            return false;
        }

        if (count == 1) {
            new CharacterPickerDialog(view.getContext(),
                                      view, content, set, insert).show();
        }

        return true;
    
private static java.lang.StringtoTitleCase(java.lang.String src)

        return Character.toUpperCase(src.charAt(0)) + src.substring(1);