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 è. 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