1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import android.text.method.MetaKeyKeyListener;
20import android.util.SparseIntArray;
21import android.os.RemoteException;
22import android.os.ServiceManager;
23import android.os.SystemClock;
24import android.util.SparseArray;
25
26import java.lang.Character;
27import java.lang.ref.WeakReference;
28
29/**
30 * Describes the keys provided by a device and their associated labels.
31 */
32public class KeyCharacterMap
33{
34    /**
35     * The id of the device's primary built in keyboard is always 0.
36     */
37    public static final int BUILT_IN_KEYBOARD = 0;
38
39    /** A numeric (12-key) keyboard. */
40    public static final int NUMERIC = 1;
41
42    /** A keyboard with all the letters, but with more than one letter
43     *  per key. */
44    public static final int PREDICTIVE = 2;
45
46    /** A keyboard with all the letters, and maybe some numbers. */
47    public static final int ALPHA = 3;
48
49    /**
50     * This private-use character is used to trigger Unicode character
51     * input by hex digits.
52     */
53    public static final char HEX_INPUT = '\uEF00';
54
55    /**
56     * This private-use character is used to bring up a character picker for
57     * miscellaneous symbols.
58     */
59    public static final char PICKER_DIALOG_INPUT = '\uEF01';
60
61    private static Object sLock = new Object();
62    private static SparseArray<WeakReference<KeyCharacterMap>> sInstances
63        = new SparseArray<WeakReference<KeyCharacterMap>>();
64
65    /**
66     * Loads the key character maps for the keyboard with the specified device id.
67     * @param keyboard The device id of the keyboard.
68     * @return The associated key character map.
69     */
70    public static KeyCharacterMap load(int keyboard)
71    {
72        synchronized (sLock) {
73            KeyCharacterMap result;
74            WeakReference<KeyCharacterMap> ref = sInstances.get(keyboard);
75            if (ref != null) {
76                result = ref.get();
77                if (result != null) {
78                    return result;
79                }
80            }
81            result = new KeyCharacterMap(keyboard);
82            sInstances.put(keyboard, new WeakReference<KeyCharacterMap>(result));
83            return result;
84        }
85    }
86
87    private KeyCharacterMap(int keyboardDevice)
88    {
89        mKeyboardDevice = keyboardDevice;
90        mPointer = ctor_native(keyboardDevice);
91    }
92
93    /**
94     * <p>
95     * Returns the Unicode character that the specified key would produce
96     * when the specified meta bits (see {@link MetaKeyKeyListener})
97     * were active.
98     * </p><p>
99     * Returns 0 if the key is not one that is used to type Unicode
100     * characters.
101     * </p><p>
102     * If the return value has bit {@link #COMBINING_ACCENT} set, the
103     * key is a "dead key" that should be combined with another to
104     * actually produce a character -- see {@link #getDeadChar} --
105     * after masking with {@link #COMBINING_ACCENT_MASK}.
106     * </p>
107     */
108    public int get(int keyCode, int meta)
109    {
110        if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
111            meta |= KeyEvent.META_SHIFT_ON;
112        }
113        if ((meta & MetaKeyKeyListener.META_ALT_LOCKED) != 0) {
114            meta |= KeyEvent.META_ALT_ON;
115        }
116
117        // Ignore caps lock on keys where alt and shift have the same effect.
118        if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
119            if (get_native(mPointer, keyCode, KeyEvent.META_SHIFT_ON) ==
120                get_native(mPointer, keyCode, KeyEvent.META_ALT_ON)) {
121                meta &= ~KeyEvent.META_SHIFT_ON;
122            }
123        }
124
125        int ret = get_native(mPointer, keyCode, meta);
126        int map = COMBINING.get(ret);
127
128        if (map != 0) {
129            return map;
130        } else {
131            return ret;
132        }
133    }
134
135    /**
136     * Gets the number or symbol associated with the key.  The character value
137     * is returned, not the numeric value.  If the key is not a number, but is
138     * a symbol, the symbol is retuned.
139     */
140    public char getNumber(int keyCode)
141    {
142        return getNumber_native(mPointer, keyCode);
143    }
144
145    /**
146     * The same as {@link #getMatch(int,char[],int) getMatch(keyCode, chars, 0)}.
147     */
148    public char getMatch(int keyCode, char[] chars)
149    {
150        return getMatch(keyCode, chars, 0);
151    }
152
153    /**
154     * If one of the chars in the array can be generated by keyCode,
155     * return the char; otherwise return '\0'.
156     * @param keyCode the key code to look at
157     * @param chars the characters to try to find
158     * @param modifiers the modifier bits to prefer.  If any of these bits
159     *                  are set, if there are multiple choices, that could
160     *                  work, the one for this modifier will be set.
161     */
162    public char getMatch(int keyCode, char[] chars, int modifiers)
163    {
164        if (chars == null) {
165            // catch it here instead of in native
166            throw new NullPointerException();
167        }
168        return getMatch_native(mPointer, keyCode, chars, modifiers);
169    }
170
171    /**
172     * Get the primary character for this key.  In other words, the label
173     * that is physically printed on it.
174     */
175    public char getDisplayLabel(int keyCode)
176    {
177        return getDisplayLabel_native(mPointer, keyCode);
178    }
179
180    /**
181     * Get the character that is produced by putting accent on the character
182     * c.
183     * For example, getDeadChar('`', 'e') returns &egrave;.
184     */
185    public static int getDeadChar(int accent, int c)
186    {
187        return DEAD.get((accent << 16) | c);
188    }
189
190    public static class KeyData {
191        public static final int META_LENGTH = 4;
192
193        /**
194         * The display label (see {@link #getDisplayLabel}).
195         */
196        public char displayLabel;
197        /**
198         * The "number" value (see {@link #getNumber}).
199         */
200        public char number;
201        /**
202         * The character that will be generated in various meta states
203         * (the same ones used for {@link #get} and defined as
204         * {@link KeyEvent#META_SHIFT_ON} and {@link KeyEvent#META_ALT_ON}).
205         *      <table>
206         *          <tr><th>Index</th><th align="left">Value</th></tr>
207         *          <tr><td>0</td><td>no modifiers</td></tr>
208         *          <tr><td>1</td><td>caps</td></tr>
209         *          <tr><td>2</td><td>alt</td></tr>
210         *          <tr><td>3</td><td>caps + alt</td></tr>
211         *      </table>
212         */
213        public char[] meta = new char[META_LENGTH];
214    }
215
216    /**
217     * Get the characters conversion data for a given keyCode.
218     *
219     * @param keyCode the keyCode to look for
220     * @param results a {@link KeyData} that will be filled with the results.
221     *
222     * @return whether the key was mapped or not.  If the key was not mapped,
223     *         results is not modified.
224     */
225    public boolean getKeyData(int keyCode, KeyData results)
226    {
227        if (results.meta.length >= KeyData.META_LENGTH) {
228            return getKeyData_native(mPointer, keyCode, results);
229        } else {
230            throw new IndexOutOfBoundsException("results.meta.length must be >= " +
231                                                KeyData.META_LENGTH);
232        }
233    }
234
235    /**
236     * Get an array of KeyEvent objects that if put into the input stream
237     * could plausibly generate the provided sequence of characters.  It is
238     * not guaranteed that the sequence is the only way to generate these
239     * events or that it is optimal.
240     *
241     * @return an array of KeyEvent objects, or null if the given char array
242     *         can not be generated using the current key character map.
243     */
244    public KeyEvent[] getEvents(char[] chars)
245    {
246        if (chars == null) {
247            throw new NullPointerException();
248        }
249
250        long[] keys = getEvents_native(mPointer, chars);
251        if (keys == null) {
252            return null;
253        }
254
255        // how big should the array be
256        int len = keys.length*2;
257        int N = keys.length;
258        for (int i=0; i<N; i++) {
259            int mods = (int)(keys[i] >> 32);
260            if ((mods & KeyEvent.META_ALT_ON) != 0) {
261                len += 2;
262            }
263            if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
264                len += 2;
265            }
266            if ((mods & KeyEvent.META_SYM_ON) != 0) {
267                len += 2;
268            }
269        }
270
271        // create the events
272        KeyEvent[] rv = new KeyEvent[len];
273        int index = 0;
274        long now = SystemClock.uptimeMillis();
275        int device = mKeyboardDevice;
276        for (int i=0; i<N; i++) {
277            int mods = (int)(keys[i] >> 32);
278            int meta = 0;
279
280            if ((mods & KeyEvent.META_ALT_ON) != 0) {
281                meta |= KeyEvent.META_ALT_ON;
282                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
283                        KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0);
284                index++;
285            }
286            if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
287                meta |= KeyEvent.META_SHIFT_ON;
288                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
289                        KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0);
290                index++;
291            }
292            if ((mods & KeyEvent.META_SYM_ON) != 0) {
293                meta |= KeyEvent.META_SYM_ON;
294                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
295                        KeyEvent.KEYCODE_SYM, 0, meta, device, 0);
296                index++;
297            }
298
299            int key = (int)(keys[i]);
300            rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
301                    key, 0, meta, device, 0);
302            index++;
303            rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
304                    key, 0, meta, device, 0);
305            index++;
306
307            if ((mods & KeyEvent.META_ALT_ON) != 0) {
308                meta &= ~KeyEvent.META_ALT_ON;
309                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
310                        KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0);
311                index++;
312            }
313            if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
314                meta &= ~KeyEvent.META_SHIFT_ON;
315                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
316                        KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0);
317                index++;
318            }
319            if ((mods & KeyEvent.META_SYM_ON) != 0) {
320                meta &= ~KeyEvent.META_SYM_ON;
321                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
322                        KeyEvent.KEYCODE_SYM, 0, meta, device, 0);
323                index++;
324            }
325        }
326
327        return rv;
328    }
329
330    /**
331     * Does this character key produce a glyph?
332     */
333    public boolean isPrintingKey(int keyCode)
334    {
335        int type = Character.getType(get(keyCode, 0));
336
337        switch (type)
338        {
339            case Character.SPACE_SEPARATOR:
340            case Character.LINE_SEPARATOR:
341            case Character.PARAGRAPH_SEPARATOR:
342            case Character.CONTROL:
343            case Character.FORMAT:
344                return false;
345            default:
346                return true;
347        }
348    }
349
350    protected void finalize() throws Throwable
351    {
352        dtor_native(mPointer);
353    }
354
355    /**
356     * Returns {@link #NUMERIC}, {@link #PREDICTIVE} or {@link #ALPHA}.
357     */
358    public int getKeyboardType()
359    {
360        return getKeyboardType_native(mPointer);
361    }
362
363    /**
364     * Queries the framework about whether any physical keys exist on the
365     * device that are capable of producing the given key codes.
366     */
367    public static boolean deviceHasKey(int keyCode) {
368        int[] codeArray = new int[1];
369        codeArray[0] = keyCode;
370        boolean[] ret = deviceHasKeys(codeArray);
371        return ret[0];
372    }
373
374    public static boolean[] deviceHasKeys(int[] keyCodes) {
375        boolean[] ret = new boolean[keyCodes.length];
376        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
377        try {
378            wm.hasKeys(keyCodes, ret);
379        } catch (RemoteException e) {
380            // no fallback; just return the empty array
381        }
382        return ret;
383    }
384
385    private int mPointer;
386    private int mKeyboardDevice;
387
388    private static native int ctor_native(int id);
389    private static native void dtor_native(int ptr);
390    private static native char get_native(int ptr, int keycode,
391                                    int meta);
392    private static native char getNumber_native(int ptr, int keycode);
393    private static native char getMatch_native(int ptr, int keycode,
394                                    char[] chars, int modifiers);
395    private static native char getDisplayLabel_native(int ptr, int keycode);
396    private static native boolean getKeyData_native(int ptr, int keycode,
397                                    KeyData results);
398    private static native int getKeyboardType_native(int ptr);
399    private static native long[] getEvents_native(int ptr, char[] str);
400
401    /**
402     * Maps Unicode combining diacritical to display-form dead key
403     * (display character shifted left 16 bits).
404     */
405    private static SparseIntArray COMBINING = new SparseIntArray();
406
407    /**
408     * Maps combinations of (display-form) dead key and second character
409     * to combined output character.
410     */
411    private static SparseIntArray DEAD = new SparseIntArray();
412
413    /*
414     * TODO: Change the table format to support full 21-bit-wide
415     * accent characters and combined characters if ever necessary.
416     */
417    private static final int ACUTE = '\u00B4' << 16;
418    private static final int GRAVE = '`' << 16;
419    private static final int CIRCUMFLEX = '^' << 16;
420    private static final int TILDE = '~' << 16;
421    private static final int UMLAUT = '\u00A8' << 16;
422
423    /*
424     * This bit will be set in the return value of {@link #get(int, int)} if the
425     * key is a "dead key."
426     */
427    public static final int COMBINING_ACCENT = 0x80000000;
428    /**
429     * Mask the return value from {@link #get(int, int)} with this value to get
430     * a printable representation of the accent character of a "dead key."
431     */
432    public static final int COMBINING_ACCENT_MASK = 0x7FFFFFFF;
433
434    static {
435        COMBINING.put('\u0300', (GRAVE >> 16) | COMBINING_ACCENT);
436        COMBINING.put('\u0301', (ACUTE >> 16) | COMBINING_ACCENT);
437        COMBINING.put('\u0302', (CIRCUMFLEX >> 16) | COMBINING_ACCENT);
438        COMBINING.put('\u0303', (TILDE >> 16) | COMBINING_ACCENT);
439        COMBINING.put('\u0308', (UMLAUT >> 16) | COMBINING_ACCENT);
440
441        DEAD.put(ACUTE | 'A', '\u00C1');
442        DEAD.put(ACUTE | 'C', '\u0106');
443        DEAD.put(ACUTE | 'E', '\u00C9');
444        DEAD.put(ACUTE | 'G', '\u01F4');
445        DEAD.put(ACUTE | 'I', '\u00CD');
446        DEAD.put(ACUTE | 'K', '\u1E30');
447        DEAD.put(ACUTE | 'L', '\u0139');
448        DEAD.put(ACUTE | 'M', '\u1E3E');
449        DEAD.put(ACUTE | 'N', '\u0143');
450        DEAD.put(ACUTE | 'O', '\u00D3');
451        DEAD.put(ACUTE | 'P', '\u1E54');
452        DEAD.put(ACUTE | 'R', '\u0154');
453        DEAD.put(ACUTE | 'S', '\u015A');
454        DEAD.put(ACUTE | 'U', '\u00DA');
455        DEAD.put(ACUTE | 'W', '\u1E82');
456        DEAD.put(ACUTE | 'Y', '\u00DD');
457        DEAD.put(ACUTE | 'Z', '\u0179');
458        DEAD.put(ACUTE | 'a', '\u00E1');
459        DEAD.put(ACUTE | 'c', '\u0107');
460        DEAD.put(ACUTE | 'e', '\u00E9');
461        DEAD.put(ACUTE | 'g', '\u01F5');
462        DEAD.put(ACUTE | 'i', '\u00ED');
463        DEAD.put(ACUTE | 'k', '\u1E31');
464        DEAD.put(ACUTE | 'l', '\u013A');
465        DEAD.put(ACUTE | 'm', '\u1E3F');
466        DEAD.put(ACUTE | 'n', '\u0144');
467        DEAD.put(ACUTE | 'o', '\u00F3');
468        DEAD.put(ACUTE | 'p', '\u1E55');
469        DEAD.put(ACUTE | 'r', '\u0155');
470        DEAD.put(ACUTE | 's', '\u015B');
471        DEAD.put(ACUTE | 'u', '\u00FA');
472        DEAD.put(ACUTE | 'w', '\u1E83');
473        DEAD.put(ACUTE | 'y', '\u00FD');
474        DEAD.put(ACUTE | 'z', '\u017A');
475        DEAD.put(CIRCUMFLEX | 'A', '\u00C2');
476        DEAD.put(CIRCUMFLEX | 'C', '\u0108');
477        DEAD.put(CIRCUMFLEX | 'E', '\u00CA');
478        DEAD.put(CIRCUMFLEX | 'G', '\u011C');
479        DEAD.put(CIRCUMFLEX | 'H', '\u0124');
480        DEAD.put(CIRCUMFLEX | 'I', '\u00CE');
481        DEAD.put(CIRCUMFLEX | 'J', '\u0134');
482        DEAD.put(CIRCUMFLEX | 'O', '\u00D4');
483        DEAD.put(CIRCUMFLEX | 'S', '\u015C');
484        DEAD.put(CIRCUMFLEX | 'U', '\u00DB');
485        DEAD.put(CIRCUMFLEX | 'W', '\u0174');
486        DEAD.put(CIRCUMFLEX | 'Y', '\u0176');
487        DEAD.put(CIRCUMFLEX | 'Z', '\u1E90');
488        DEAD.put(CIRCUMFLEX | 'a', '\u00E2');
489        DEAD.put(CIRCUMFLEX | 'c', '\u0109');
490        DEAD.put(CIRCUMFLEX | 'e', '\u00EA');
491        DEAD.put(CIRCUMFLEX | 'g', '\u011D');
492        DEAD.put(CIRCUMFLEX | 'h', '\u0125');
493        DEAD.put(CIRCUMFLEX | 'i', '\u00EE');
494        DEAD.put(CIRCUMFLEX | 'j', '\u0135');
495        DEAD.put(CIRCUMFLEX | 'o', '\u00F4');
496        DEAD.put(CIRCUMFLEX | 's', '\u015D');
497        DEAD.put(CIRCUMFLEX | 'u', '\u00FB');
498        DEAD.put(CIRCUMFLEX | 'w', '\u0175');
499        DEAD.put(CIRCUMFLEX | 'y', '\u0177');
500        DEAD.put(CIRCUMFLEX | 'z', '\u1E91');
501        DEAD.put(GRAVE | 'A', '\u00C0');
502        DEAD.put(GRAVE | 'E', '\u00C8');
503        DEAD.put(GRAVE | 'I', '\u00CC');
504        DEAD.put(GRAVE | 'N', '\u01F8');
505        DEAD.put(GRAVE | 'O', '\u00D2');
506        DEAD.put(GRAVE | 'U', '\u00D9');
507        DEAD.put(GRAVE | 'W', '\u1E80');
508        DEAD.put(GRAVE | 'Y', '\u1EF2');
509        DEAD.put(GRAVE | 'a', '\u00E0');
510        DEAD.put(GRAVE | 'e', '\u00E8');
511        DEAD.put(GRAVE | 'i', '\u00EC');
512        DEAD.put(GRAVE | 'n', '\u01F9');
513        DEAD.put(GRAVE | 'o', '\u00F2');
514        DEAD.put(GRAVE | 'u', '\u00F9');
515        DEAD.put(GRAVE | 'w', '\u1E81');
516        DEAD.put(GRAVE | 'y', '\u1EF3');
517        DEAD.put(TILDE | 'A', '\u00C3');
518        DEAD.put(TILDE | 'E', '\u1EBC');
519        DEAD.put(TILDE | 'I', '\u0128');
520        DEAD.put(TILDE | 'N', '\u00D1');
521        DEAD.put(TILDE | 'O', '\u00D5');
522        DEAD.put(TILDE | 'U', '\u0168');
523        DEAD.put(TILDE | 'V', '\u1E7C');
524        DEAD.put(TILDE | 'Y', '\u1EF8');
525        DEAD.put(TILDE | 'a', '\u00E3');
526        DEAD.put(TILDE | 'e', '\u1EBD');
527        DEAD.put(TILDE | 'i', '\u0129');
528        DEAD.put(TILDE | 'n', '\u00F1');
529        DEAD.put(TILDE | 'o', '\u00F5');
530        DEAD.put(TILDE | 'u', '\u0169');
531        DEAD.put(TILDE | 'v', '\u1E7D');
532        DEAD.put(TILDE | 'y', '\u1EF9');
533        DEAD.put(UMLAUT | 'A', '\u00C4');
534        DEAD.put(UMLAUT | 'E', '\u00CB');
535        DEAD.put(UMLAUT | 'H', '\u1E26');
536        DEAD.put(UMLAUT | 'I', '\u00CF');
537        DEAD.put(UMLAUT | 'O', '\u00D6');
538        DEAD.put(UMLAUT | 'U', '\u00DC');
539        DEAD.put(UMLAUT | 'W', '\u1E84');
540        DEAD.put(UMLAUT | 'X', '\u1E8C');
541        DEAD.put(UMLAUT | 'Y', '\u0178');
542        DEAD.put(UMLAUT | 'a', '\u00E4');
543        DEAD.put(UMLAUT | 'e', '\u00EB');
544        DEAD.put(UMLAUT | 'h', '\u1E27');
545        DEAD.put(UMLAUT | 'i', '\u00EF');
546        DEAD.put(UMLAUT | 'o', '\u00F6');
547        DEAD.put(UMLAUT | 't', '\u1E97');
548        DEAD.put(UMLAUT | 'u', '\u00FC');
549        DEAD.put(UMLAUT | 'w', '\u1E85');
550        DEAD.put(UMLAUT | 'x', '\u1E8D');
551        DEAD.put(UMLAUT | 'y', '\u00FF');
552    }
553}
554