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