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