2424import android .hardware .input .InputManager ;
2525
2626import java .lang .Character ;
27+ import java .text .Normalizer ;
2728
2829/**
2930 * Describes the keys provided by a keyboard device and their associated labels.
@@ -149,9 +150,22 @@ public class KeyCharacterMap implements Parcelable {
149150
150151 /* Characters used to display placeholders for dead keys. */
151152 private static final int ACCENT_ACUTE = '\u00B4' ;
153+ private static final int ACCENT_BREVE = '\u02D8' ;
154+ private static final int ACCENT_CARON = '\u02C7' ;
155+ private static final int ACCENT_CEDILLA = '\u00B8' ;
156+ private static final int ACCENT_COMMA_ABOVE = '\u1FBD' ;
157+ private static final int ACCENT_COMMA_ABOVE_RIGHT = '\u02BC' ;
158+ private static final int ACCENT_DOT_ABOVE = '\u02D9' ;
159+ private static final int ACCENT_DOUBLE_ACUTE = '\u02DD' ;
152160 private static final int ACCENT_GRAVE = '\u02CB' ;
153161 private static final int ACCENT_CIRCUMFLEX = '\u02C6' ;
162+ private static final int ACCENT_MACRON = '\u00AF' ;
163+ private static final int ACCENT_MACRON_BELOW = '\u02CD' ;
164+ private static final int ACCENT_OGONEK = '\u02DB' ;
165+ private static final int ACCENT_REVERSED_COMMA_ABOVE = '\u02BD' ;
166+ private static final int ACCENT_RING_ABOVE = '\u02DA' ;
154167 private static final int ACCENT_TILDE = '\u02DC' ;
168+ private static final int ACCENT_TURNED_COMMA_ABOVE = '\u02BB' ;
155169 private static final int ACCENT_UMLAUT = '\u00A8' ;
156170
157171 /* Legacy dead key display characters used in previous versions of the API.
@@ -161,136 +175,66 @@ public class KeyCharacterMap implements Parcelable {
161175 private static final int ACCENT_TILDE_LEGACY = '~' ;
162176
163177 /**
164- * Maps Unicode combining diacritical to display-form dead key
165- * (display character shifted left 16 bits).
178+ * Maps Unicode combining diacritical to display-form dead key.
166179 */
167- private static final SparseIntArray COMBINING = new SparseIntArray ();
180+ private static final SparseIntArray sCombiningToAccent = new SparseIntArray ();
181+ private static final SparseIntArray sAccentToCombining = new SparseIntArray ();
168182 static {
169- COMBINING .put ('\u0300' , ACCENT_GRAVE );
170- COMBINING .put ('\u0301' , ACCENT_ACUTE );
171- COMBINING .put ('\u0302' , ACCENT_CIRCUMFLEX );
172- COMBINING .put ('\u0303' , ACCENT_TILDE );
173- COMBINING .put ('\u0308' , ACCENT_UMLAUT );
183+ addCombining ('\u0300' , ACCENT_GRAVE );
184+ addCombining ('\u0301' , ACCENT_ACUTE );
185+ addCombining ('\u0302' , ACCENT_CIRCUMFLEX );
186+ addCombining ('\u0303' , ACCENT_TILDE );
187+ addCombining ('\u0304' , ACCENT_MACRON );
188+ addCombining ('\u0306' , ACCENT_BREVE );
189+ addCombining ('\u0307' , ACCENT_DOT_ABOVE );
190+ addCombining ('\u0308' , ACCENT_UMLAUT );
191+ //addCombining('\u0309', ACCENT_HOOK_ABOVE);
192+ addCombining ('\u030A' , ACCENT_RING_ABOVE );
193+ addCombining ('\u030B' , ACCENT_DOUBLE_ACUTE );
194+ addCombining ('\u030C' , ACCENT_CARON );
195+ //addCombining('\u030D', ACCENT_VERTICAL_LINE_ABOVE);
196+ //addCombining('\u030E', ACCENT_DOUBLE_VERTICAL_LINE_ABOVE);
197+ //addCombining('\u030F', ACCENT_DOUBLE_GRAVE);
198+ //addCombining('\u0310', ACCENT_CANDRABINDU);
199+ //addCombining('\u0311', ACCENT_INVERTED_BREVE);
200+ addCombining ('\u0312' , ACCENT_TURNED_COMMA_ABOVE );
201+ addCombining ('\u0313' , ACCENT_COMMA_ABOVE );
202+ addCombining ('\u0314' , ACCENT_REVERSED_COMMA_ABOVE );
203+ addCombining ('\u0315' , ACCENT_COMMA_ABOVE_RIGHT );
204+ //addCombining('\u031B', ACCENT_HORN);
205+ //addCombining('\u0323', ACCENT_DOT_BELOW);
206+ //addCombining('\u0326', ACCENT_COMMA_BELOW);
207+ addCombining ('\u0327' , ACCENT_CEDILLA );
208+ addCombining ('\u0328' , ACCENT_OGONEK );
209+ //addCombining('\u0329', ACCENT_VERTICAL_LINE_BELOW);
210+ addCombining ('\u0331' , ACCENT_MACRON_BELOW );
211+ //addCombining('\u0342', ACCENT_PERISPOMENI);
212+ //addCombining('\u0344', ACCENT_DIALYTIKA_TONOS);
213+ //addCombining('\u0345', ACCENT_YPOGEGRAMMENI);
214+
215+ // One-way mappings to equivalent preferred accents.
216+ sCombiningToAccent .append ('\u0340' , ACCENT_GRAVE );
217+ sCombiningToAccent .append ('\u0341' , ACCENT_ACUTE );
218+ sCombiningToAccent .append ('\u0343' , ACCENT_COMMA_ABOVE );
219+
220+ // One-way legacy mappings to preserve compatibility with older applications.
221+ sAccentToCombining .append (ACCENT_GRAVE_LEGACY , '\u0300' );
222+ sAccentToCombining .append (ACCENT_CIRCUMFLEX_LEGACY , '\u0302' );
223+ sAccentToCombining .append (ACCENT_TILDE_LEGACY , '\u0303' );
224+ }
225+
226+ private static void addCombining (int combining , int accent ) {
227+ sCombiningToAccent .append (combining , accent );
228+ sAccentToCombining .append (accent , combining );
174229 }
175230
176231 /**
177- * Maps combinations of (display-form) dead key and second character
232+ * Maps combinations of (display-form) combining key and second character
178233 * to combined output character.
234+ * These mappings are derived from the Unicode NFC tables as needed.
179235 */
180- private static final SparseIntArray DEAD = new SparseIntArray ();
181- static {
182- addDeadChar (ACCENT_ACUTE , 'A' , '\u00C1' );
183- addDeadChar (ACCENT_ACUTE , 'C' , '\u0106' );
184- addDeadChar (ACCENT_ACUTE , 'E' , '\u00C9' );
185- addDeadChar (ACCENT_ACUTE , 'G' , '\u01F4' );
186- addDeadChar (ACCENT_ACUTE , 'I' , '\u00CD' );
187- addDeadChar (ACCENT_ACUTE , 'K' , '\u1E30' );
188- addDeadChar (ACCENT_ACUTE , 'L' , '\u0139' );
189- addDeadChar (ACCENT_ACUTE , 'M' , '\u1E3E' );
190- addDeadChar (ACCENT_ACUTE , 'N' , '\u0143' );
191- addDeadChar (ACCENT_ACUTE , 'O' , '\u00D3' );
192- addDeadChar (ACCENT_ACUTE , 'P' , '\u1E54' );
193- addDeadChar (ACCENT_ACUTE , 'R' , '\u0154' );
194- addDeadChar (ACCENT_ACUTE , 'S' , '\u015A' );
195- addDeadChar (ACCENT_ACUTE , 'U' , '\u00DA' );
196- addDeadChar (ACCENT_ACUTE , 'W' , '\u1E82' );
197- addDeadChar (ACCENT_ACUTE , 'Y' , '\u00DD' );
198- addDeadChar (ACCENT_ACUTE , 'Z' , '\u0179' );
199- addDeadChar (ACCENT_ACUTE , 'a' , '\u00E1' );
200- addDeadChar (ACCENT_ACUTE , 'c' , '\u0107' );
201- addDeadChar (ACCENT_ACUTE , 'e' , '\u00E9' );
202- addDeadChar (ACCENT_ACUTE , 'g' , '\u01F5' );
203- addDeadChar (ACCENT_ACUTE , 'i' , '\u00ED' );
204- addDeadChar (ACCENT_ACUTE , 'k' , '\u1E31' );
205- addDeadChar (ACCENT_ACUTE , 'l' , '\u013A' );
206- addDeadChar (ACCENT_ACUTE , 'm' , '\u1E3F' );
207- addDeadChar (ACCENT_ACUTE , 'n' , '\u0144' );
208- addDeadChar (ACCENT_ACUTE , 'o' , '\u00F3' );
209- addDeadChar (ACCENT_ACUTE , 'p' , '\u1E55' );
210- addDeadChar (ACCENT_ACUTE , 'r' , '\u0155' );
211- addDeadChar (ACCENT_ACUTE , 's' , '\u015B' );
212- addDeadChar (ACCENT_ACUTE , 'u' , '\u00FA' );
213- addDeadChar (ACCENT_ACUTE , 'w' , '\u1E83' );
214- addDeadChar (ACCENT_ACUTE , 'y' , '\u00FD' );
215- addDeadChar (ACCENT_ACUTE , 'z' , '\u017A' );
216- addDeadChar (ACCENT_CIRCUMFLEX , 'A' , '\u00C2' );
217- addDeadChar (ACCENT_CIRCUMFLEX , 'C' , '\u0108' );
218- addDeadChar (ACCENT_CIRCUMFLEX , 'E' , '\u00CA' );
219- addDeadChar (ACCENT_CIRCUMFLEX , 'G' , '\u011C' );
220- addDeadChar (ACCENT_CIRCUMFLEX , 'H' , '\u0124' );
221- addDeadChar (ACCENT_CIRCUMFLEX , 'I' , '\u00CE' );
222- addDeadChar (ACCENT_CIRCUMFLEX , 'J' , '\u0134' );
223- addDeadChar (ACCENT_CIRCUMFLEX , 'O' , '\u00D4' );
224- addDeadChar (ACCENT_CIRCUMFLEX , 'S' , '\u015C' );
225- addDeadChar (ACCENT_CIRCUMFLEX , 'U' , '\u00DB' );
226- addDeadChar (ACCENT_CIRCUMFLEX , 'W' , '\u0174' );
227- addDeadChar (ACCENT_CIRCUMFLEX , 'Y' , '\u0176' );
228- addDeadChar (ACCENT_CIRCUMFLEX , 'Z' , '\u1E90' );
229- addDeadChar (ACCENT_CIRCUMFLEX , 'a' , '\u00E2' );
230- addDeadChar (ACCENT_CIRCUMFLEX , 'c' , '\u0109' );
231- addDeadChar (ACCENT_CIRCUMFLEX , 'e' , '\u00EA' );
232- addDeadChar (ACCENT_CIRCUMFLEX , 'g' , '\u011D' );
233- addDeadChar (ACCENT_CIRCUMFLEX , 'h' , '\u0125' );
234- addDeadChar (ACCENT_CIRCUMFLEX , 'i' , '\u00EE' );
235- addDeadChar (ACCENT_CIRCUMFLEX , 'j' , '\u0135' );
236- addDeadChar (ACCENT_CIRCUMFLEX , 'o' , '\u00F4' );
237- addDeadChar (ACCENT_CIRCUMFLEX , 's' , '\u015D' );
238- addDeadChar (ACCENT_CIRCUMFLEX , 'u' , '\u00FB' );
239- addDeadChar (ACCENT_CIRCUMFLEX , 'w' , '\u0175' );
240- addDeadChar (ACCENT_CIRCUMFLEX , 'y' , '\u0177' );
241- addDeadChar (ACCENT_CIRCUMFLEX , 'z' , '\u1E91' );
242- addDeadChar (ACCENT_GRAVE , 'A' , '\u00C0' );
243- addDeadChar (ACCENT_GRAVE , 'E' , '\u00C8' );
244- addDeadChar (ACCENT_GRAVE , 'I' , '\u00CC' );
245- addDeadChar (ACCENT_GRAVE , 'N' , '\u01F8' );
246- addDeadChar (ACCENT_GRAVE , 'O' , '\u00D2' );
247- addDeadChar (ACCENT_GRAVE , 'U' , '\u00D9' );
248- addDeadChar (ACCENT_GRAVE , 'W' , '\u1E80' );
249- addDeadChar (ACCENT_GRAVE , 'Y' , '\u1EF2' );
250- addDeadChar (ACCENT_GRAVE , 'a' , '\u00E0' );
251- addDeadChar (ACCENT_GRAVE , 'e' , '\u00E8' );
252- addDeadChar (ACCENT_GRAVE , 'i' , '\u00EC' );
253- addDeadChar (ACCENT_GRAVE , 'n' , '\u01F9' );
254- addDeadChar (ACCENT_GRAVE , 'o' , '\u00F2' );
255- addDeadChar (ACCENT_GRAVE , 'u' , '\u00F9' );
256- addDeadChar (ACCENT_GRAVE , 'w' , '\u1E81' );
257- addDeadChar (ACCENT_GRAVE , 'y' , '\u1EF3' );
258- addDeadChar (ACCENT_TILDE , 'A' , '\u00C3' );
259- addDeadChar (ACCENT_TILDE , 'E' , '\u1EBC' );
260- addDeadChar (ACCENT_TILDE , 'I' , '\u0128' );
261- addDeadChar (ACCENT_TILDE , 'N' , '\u00D1' );
262- addDeadChar (ACCENT_TILDE , 'O' , '\u00D5' );
263- addDeadChar (ACCENT_TILDE , 'U' , '\u0168' );
264- addDeadChar (ACCENT_TILDE , 'V' , '\u1E7C' );
265- addDeadChar (ACCENT_TILDE , 'Y' , '\u1EF8' );
266- addDeadChar (ACCENT_TILDE , 'a' , '\u00E3' );
267- addDeadChar (ACCENT_TILDE , 'e' , '\u1EBD' );
268- addDeadChar (ACCENT_TILDE , 'i' , '\u0129' );
269- addDeadChar (ACCENT_TILDE , 'n' , '\u00F1' );
270- addDeadChar (ACCENT_TILDE , 'o' , '\u00F5' );
271- addDeadChar (ACCENT_TILDE , 'u' , '\u0169' );
272- addDeadChar (ACCENT_TILDE , 'v' , '\u1E7D' );
273- addDeadChar (ACCENT_TILDE , 'y' , '\u1EF9' );
274- addDeadChar (ACCENT_UMLAUT , 'A' , '\u00C4' );
275- addDeadChar (ACCENT_UMLAUT , 'E' , '\u00CB' );
276- addDeadChar (ACCENT_UMLAUT , 'H' , '\u1E26' );
277- addDeadChar (ACCENT_UMLAUT , 'I' , '\u00CF' );
278- addDeadChar (ACCENT_UMLAUT , 'O' , '\u00D6' );
279- addDeadChar (ACCENT_UMLAUT , 'U' , '\u00DC' );
280- addDeadChar (ACCENT_UMLAUT , 'W' , '\u1E84' );
281- addDeadChar (ACCENT_UMLAUT , 'X' , '\u1E8C' );
282- addDeadChar (ACCENT_UMLAUT , 'Y' , '\u0178' );
283- addDeadChar (ACCENT_UMLAUT , 'a' , '\u00E4' );
284- addDeadChar (ACCENT_UMLAUT , 'e' , '\u00EB' );
285- addDeadChar (ACCENT_UMLAUT , 'h' , '\u1E27' );
286- addDeadChar (ACCENT_UMLAUT , 'i' , '\u00EF' );
287- addDeadChar (ACCENT_UMLAUT , 'o' , '\u00F6' );
288- addDeadChar (ACCENT_UMLAUT , 't' , '\u1E97' );
289- addDeadChar (ACCENT_UMLAUT , 'u' , '\u00FC' );
290- addDeadChar (ACCENT_UMLAUT , 'w' , '\u1E85' );
291- addDeadChar (ACCENT_UMLAUT , 'x' , '\u1E8D' );
292- addDeadChar (ACCENT_UMLAUT , 'y' , '\u00FF' );
293- }
236+ private static final SparseIntArray sDeadKeyCache = new SparseIntArray ();
237+ private static final StringBuilder sDeadKeyBuilder = new StringBuilder ();
294238
295239 public static final Parcelable .Creator <KeyCharacterMap > CREATOR =
296240 new Parcelable .Creator <KeyCharacterMap >() {
@@ -387,7 +331,7 @@ public int get(int keyCode, int metaState) {
387331 metaState = KeyEvent .normalizeMetaState (metaState );
388332 char ch = nativeGetCharacter (mPtr , keyCode , metaState );
389333
390- int map = COMBINING .get (ch );
334+ int map = sCombiningToAccent .get (ch );
391335 if (map != 0 ) {
392336 return map | COMBINING_ACCENT ;
393337 } else {
@@ -503,14 +447,25 @@ public char getDisplayLabel(int keyCode) {
503447 * @return The combined character, or 0 if the characters cannot be combined.
504448 */
505449 public static int getDeadChar (int accent , int c ) {
506- if (accent == ACCENT_CIRCUMFLEX_LEGACY ) {
507- accent = ACCENT_CIRCUMFLEX ;
508- } else if (accent == ACCENT_GRAVE_LEGACY ) {
509- accent = ACCENT_GRAVE ;
510- } else if (accent == ACCENT_TILDE_LEGACY ) {
511- accent = ACCENT_TILDE ;
450+ int combining = sAccentToCombining .get (accent );
451+ if (combining == 0 ) {
452+ return 0 ;
512453 }
513- return DEAD .get ((accent << 16 ) | c );
454+
455+ final int combination = (combining << 16 ) | c ;
456+ int combined ;
457+ synchronized (sDeadKeyCache ) {
458+ combined = sDeadKeyCache .get (combination , -1 );
459+ if (combined == -1 ) {
460+ sDeadKeyBuilder .setLength (0 );
461+ sDeadKeyBuilder .append ((char )c );
462+ sDeadKeyBuilder .append ((char )combining );
463+ String result = Normalizer .normalize (sDeadKeyBuilder , Normalizer .Form .NFC );
464+ combined = result .length () == 1 ? result .charAt (0 ) : 0 ;
465+ sDeadKeyCache .put (combination , combined );
466+ }
467+ }
468+ return combined ;
514469 }
515470
516471 /**
@@ -723,10 +678,6 @@ public int describeContents() {
723678 return 0 ;
724679 }
725680
726- private static void addDeadChar (int accent , int c , char combinedResult ) {
727- DEAD .put ((accent << 16 ) | c , combinedResult );
728- }
729-
730681 /**
731682 * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded.
732683 */
0 commit comments