diff options
39 files changed, 764 insertions, 318 deletions
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 937a79933..a4e2d2914 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -365,18 +365,12 @@ (Compact) can be an abbreviation to fit in the CHAR LIMIT. TODO: Remove translatable=false once we are settled down with the naming. --> <string name="subtype_generic_compact" translatable="false"><xliff:g id="LANGUAGE_NAME" example="Hindi">%s</xliff:g> (Compact)</string> - <!-- TODO: Uncomment once we can handle IETF language tag with script name specified. - Description for Serbian Cyrillic keyboard subtype [CHAR LIMIT=25] - (Cyrillic) can be an abbreviation to fit in the CHAR LIMIT. - <string name="subtype_serbian_cyrillic">Serbian (Cyrillic)</string> - Description for Serbian Latin keyboard subtype [CHAR LIMIT=25] - (Latin) can be an abbreviation to fit in the CHAR LIMIT. - <string name="subtype_serbian_latin">Serbian (Latin)</string> - Description for Serbian Latin keyboard subtype with explicit keyboard layout [CHAR LIMIT=25] - (Latin) can be an abbreviation to fit in the CHAR LIMIT. - This should be identical to subtype_serbian_latin aside from the trailing (%s). - <string name="subtype_with_layout_sr-Latn">Serbian (Latin) (<xliff:g id="KEYBOARD_LAYOUT" example="QWERTY">%s</xliff:g>)</string> - --> + <!-- Description for "LANGUAGE_NAME" (Cyrillic) keyboard subtype [CHAR LIMIT=25] + (Cyrillic) can be an abbreviation to fit in the CHAR LIMIT. --> + <string name="subtype_generic_cyrillic"><xliff:g id="LANGUAGE_NAME" example="Serbian">%s</xliff:g> (Cyrillic)</string> + <!-- Description for "LANGUAGE_NAME" (Latin) keyboard subtype [CHAR LIMIT=25] + (Latin) can be an abbreviation to fit in the CHAR LIMIT. --> + <string name="subtype_generic_latin"><xliff:g id="LANGUAGE_NAME" example="Serbian">%s</xliff:g> (Latin)</string> <!-- This string is displayed in a language list that allows to choose a language for suggestions in a software keyboard. This setting won't give suggestions in any particular language, hence "No language". diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 1bef3c254..594378fb4 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -40,7 +40,7 @@ eo: Esperanto/spanish es: Spanish/spanish es_US: Spanish (United States)/spanish - (es_419: Spanish (Latin America)/qwerty) + es_419: Spanish (Latin America)/spanish et_EE: Estonian (Estonia)/nordic eu_ES: Basque (Spain)/spanish fa: Persian/farsi @@ -57,6 +57,7 @@ in: Indonesian/qwerty # "id" is the official language code of Indonesian. is: Icelandic/qwerty it: Italian/qwerty + it_CH: Italian (Switzerland)/swiss iw: Hebrew/hebrew # "he" is the official language code of Hebrew. ka_GE: Georgian (Georgia)/georgian kk: Kazakh/east_slavic @@ -248,16 +249,14 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" android:isAsciiCapable="true" /> - <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" - android:subtypeId="0x623f9286" + android:subtypeId="0xa23e5d19" android:imeSubtypeLocale="es_419" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" android:isAsciiCapable="true" /> - --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xec2d3955" @@ -387,6 +386,14 @@ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" android:isAsciiCapable="true" /> + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0xd914fe1a" + android:imeSubtypeLocale="it_CH" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=swiss,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" + /> <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" diff --git a/java/res/xml/rowkeys_greek1.xml b/java/res/xml/rowkeys_greek1.xml index c5033d029..6d767794c 100644 --- a/java/res/xml/rowkeys_greek1.xml +++ b/java/res/xml/rowkeys_greek1.xml @@ -69,7 +69,7 @@ latin:keySpec="ε" latin:keyHintLabel="3" latin:additionalMoreKeys="3" - latin:moreKeys="έ" /> + latin:moreKeys="έ,%" /> <!-- U+03C1: "ρ" GREEK SMALL LETTER RHO --> <Key latin:keySpec="ρ" @@ -88,7 +88,7 @@ latin:keySpec="υ" latin:keyHintLabel="6" latin:additionalMoreKeys="6" - latin:moreKeys="ύ,ϋ,ΰ" /> + latin:moreKeys="ύ,%,ϋ,ΰ" /> <!-- U+03B8: "θ" GREEK SMALL LETTER THETA --> <Key latin:keySpec="θ" @@ -102,14 +102,14 @@ latin:keySpec="ι" latin:keyHintLabel="8" latin:additionalMoreKeys="8" - latin:moreKeys="ί,ϊ,ΐ" /> + latin:moreKeys="ί,%,ϊ,ΐ" /> <!-- U+03BF: "ο" GREEK SMALL LETTER OMICRON U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS --> <Key latin:keySpec="ο" latin:keyHintLabel="9" latin:additionalMoreKeys="9" - latin:moreKeys="ό" /> + latin:moreKeys="ό,%" /> <!-- U+03C0: "π" GREEK SMALL LETTER PI --> <Key latin:keySpec="π" diff --git a/java/src/com/android/inputmethod/compat/LooperCompatUtils.java b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java new file mode 100644 index 000000000..d647dbbd3 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.os.Looper; + +import java.lang.reflect.Method; + +/** + * Helper to call Looper#quitSafely, which was introduced in API + * level 18 (Build.VERSION_CODES.JELLY_BEAN_MR2). + * + * In unit tests, we create lots of instances of LatinIME, which means we need to clean up + * some Loopers lest we leak file descriptors. In normal use on a device though, this is never + * necessary (although it does not hurt). + */ +public final class LooperCompatUtils { + private static final Method METHOD_quitSafely = CompatUtils.getMethod( + Looper.class, "quitSafely"); + + public static void quitSafely(final Looper looper) { + if (null != METHOD_quitSafely) { + CompatUtils.invoke(looper, null /* default return value */, METHOD_quitSafely); + } else { + looper.quit(); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 2e4a0902d..949f03794 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -297,12 +297,19 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } // Implements {@link KeyboardState.SwitchActions}. + // TODO[IL]: merge the two following methods; remove the one without args. @Override public void requestUpdatingShiftState() { mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState()); } + // Future method for requesting an updating to the shift state. + public void requestUpdatingShiftState(final int currentAutoCapsState, + final int currentRecapitalizeState) { + mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState); + } + // Implements {@link KeyboardState.SwitchActions}. @Override public void startDoubleTapShiftKeyTimer() { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index 1bd98332f..37514be91 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -141,43 +141,43 @@ public final class KeyboardTextsTable { /* 51: 4 */ "additional_morekeys_symbols_8", /* 52: 4 */ "additional_morekeys_symbols_9", /* 53: 4 */ "additional_morekeys_symbols_0", - /* 54: 3 */ "morekeys_punctuation", - /* 55: 3 */ "morekeys_star", - /* 56: 3 */ "keyspec_left_parenthesis", - /* 57: 3 */ "keyspec_right_parenthesis", - /* 58: 3 */ "keyspec_left_square_bracket", - /* 59: 3 */ "keyspec_right_square_bracket", - /* 60: 3 */ "keyspec_left_curly_bracket", - /* 61: 3 */ "keyspec_right_curly_bracket", - /* 62: 3 */ "keyspec_less_than", - /* 63: 3 */ "keyspec_greater_than", - /* 64: 3 */ "keyspec_less_than_equal", - /* 65: 3 */ "keyspec_greater_than_equal", - /* 66: 3 */ "keyspec_left_double_angle_quote", - /* 67: 3 */ "keyspec_right_double_angle_quote", - /* 68: 3 */ "keyspec_left_single_angle_quote", - /* 69: 3 */ "keyspec_right_single_angle_quote", - /* 70: 3 */ "keyspec_tablet_comma", - /* 71: 3 */ "morekeys_tablet_period", - /* 72: 3 */ "morekeys_question", - /* 73: 2 */ "morekeys_h", - /* 74: 2 */ "morekeys_w", - /* 75: 2 */ "morekeys_east_slavic_row2_2", - /* 76: 2 */ "morekeys_cyrillic_u", - /* 77: 2 */ "morekeys_cyrillic_en", - /* 78: 2 */ "morekeys_cyrillic_ghe", - /* 79: 2 */ "morekeys_cyrillic_o", - /* 80: 2 */ "morekeys_cyrillic_i", - /* 81: 2 */ "keyspec_south_slavic_row1_6", - /* 82: 2 */ "keyspec_south_slavic_row2_11", - /* 83: 2 */ "keyspec_south_slavic_row3_1", - /* 84: 2 */ "keyspec_south_slavic_row3_8", - /* 85: 2 */ "keyspec_swiss_row1_11", - /* 86: 2 */ "keyspec_swiss_row2_10", - /* 87: 2 */ "keyspec_swiss_row2_11", - /* 88: 2 */ "morekeys_swiss_row1_11", - /* 89: 2 */ "morekeys_swiss_row2_10", - /* 90: 2 */ "morekeys_swiss_row2_11", + /* 54: 3 */ "keyspec_swiss_row1_11", + /* 55: 3 */ "keyspec_swiss_row2_10", + /* 56: 3 */ "keyspec_swiss_row2_11", + /* 57: 3 */ "morekeys_swiss_row1_11", + /* 58: 3 */ "morekeys_swiss_row2_10", + /* 59: 3 */ "morekeys_swiss_row2_11", + /* 60: 3 */ "morekeys_punctuation", + /* 61: 3 */ "morekeys_star", + /* 62: 3 */ "keyspec_left_parenthesis", + /* 63: 3 */ "keyspec_right_parenthesis", + /* 64: 3 */ "keyspec_left_square_bracket", + /* 65: 3 */ "keyspec_right_square_bracket", + /* 66: 3 */ "keyspec_left_curly_bracket", + /* 67: 3 */ "keyspec_right_curly_bracket", + /* 68: 3 */ "keyspec_less_than", + /* 69: 3 */ "keyspec_greater_than", + /* 70: 3 */ "keyspec_less_than_equal", + /* 71: 3 */ "keyspec_greater_than_equal", + /* 72: 3 */ "keyspec_left_double_angle_quote", + /* 73: 3 */ "keyspec_right_double_angle_quote", + /* 74: 3 */ "keyspec_left_single_angle_quote", + /* 75: 3 */ "keyspec_right_single_angle_quote", + /* 76: 3 */ "keyspec_tablet_comma", + /* 77: 3 */ "morekeys_tablet_period", + /* 78: 3 */ "morekeys_question", + /* 79: 2 */ "morekeys_h", + /* 80: 2 */ "morekeys_w", + /* 81: 2 */ "morekeys_east_slavic_row2_2", + /* 82: 2 */ "morekeys_cyrillic_u", + /* 83: 2 */ "morekeys_cyrillic_en", + /* 84: 2 */ "morekeys_cyrillic_ghe", + /* 85: 2 */ "morekeys_cyrillic_o", + /* 86: 2 */ "morekeys_cyrillic_i", + /* 87: 2 */ "keyspec_south_slavic_row1_6", + /* 88: 2 */ "keyspec_south_slavic_row2_11", + /* 89: 2 */ "keyspec_south_slavic_row3_1", + /* 90: 2 */ "keyspec_south_slavic_row3_8", /* 91: 2 */ "keyspec_spanish_row2_10", /* 92: 2 */ "morekeys_bullet", /* 93: 2 */ "morekeys_left_parenthesis", @@ -291,8 +291,9 @@ public final class KeyboardTextsTable { // Label for "switch to symbols" key. /* keylabel_to_symbol */ "?123", /* additional_morekeys_symbols_1 ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ additional_morekeys_symbols_0 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, + /* ~ morekeys_swiss_row2_11 */ /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER @@ -325,9 +326,8 @@ public final class KeyboardTextsTable { // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_question */ "\u00BF", /* morekeys_h ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ morekeys_swiss_row2_11 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~ keyspec_south_slavic_row3_8 */ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* keyspec_spanish_row2_10 */ "\u00F1", // U+266A: "♪" EIGHTH NOTE @@ -571,7 +571,9 @@ public final class KeyboardTextsTable { // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C", - /* morekeys_punctuation */ null, + /* keyspec_swiss_row1_11 ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_punctuation */ // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR /* morekeys_star */ "\u2605,\u066D", @@ -603,8 +605,7 @@ public final class KeyboardTextsTable { // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_question */ "?,\u00BF", /* morekeys_h ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_spanish_row2_10 */ // U+266A: "♪" EIGHTH NOTE /* morekeys_bullet */ "\u266A", @@ -806,15 +807,14 @@ public final class KeyboardTextsTable { /* morekeys_g ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~ additional_morekeys_symbols_0 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ morekeys_swiss_row2_11 */ // U+00B7: "·" MIDDLE DOT /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,\u00B7,#,),(,/,;,',@,:,-,\",+,\\%,&", /* morekeys_star ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~ morekeys_swiss_row2_11 */ + /* ~ keyspec_south_slavic_row3_8 */ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* keyspec_spanish_row2_10 */ "\u00E7", /* morekeys_bullet ~ */ @@ -981,7 +981,7 @@ public final class KeyboardTextsTable { // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON - /* morekeys_a */ "\u00E4,\u00E2,\u00E0,\u00E1,\u00E6,\u00E3,\u00E5,\u0101", + /* morekeys_a */ "\u00E4,%,\u00E2,\u00E0,\u00E1,\u00E6,\u00E3,\u00E5,\u0101", // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE @@ -990,13 +990,13 @@ public final class KeyboardTextsTable { // U+0153: "œ" LATIN SMALL LIGATURE OE // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON - /* morekeys_o */ "\u00F6,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u00F8,\u014D", + /* morekeys_o */ "\u00F6,%,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u00F8,\u014D", // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + /* morekeys_u */ "\u00FC,%,\u00FB,\u00F9,\u00FA,\u016B", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -1023,10 +1023,8 @@ public final class KeyboardTextsTable { /* keyspec_currency ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~ keyspec_south_slavic_row3_8 */ + null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* keyspec_swiss_row1_11 */ "\u00FC", // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS @@ -1220,7 +1218,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_question */ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE @@ -1228,9 +1226,8 @@ public final class KeyboardTextsTable { // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX /* morekeys_w */ "w,\u0175", /* morekeys_east_slavic_row2_2 ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~ morekeys_swiss_row2_11 */ + null, null, null, null, null, null, null, null, null, null, + /* ~ keyspec_south_slavic_row3_8 */ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX /* keyspec_spanish_row2_10 */ "\u0135", /* morekeys_bullet ~ */ @@ -1307,8 +1304,8 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~ additional_morekeys_symbols_0 */ + null, null, null, null, null, null, null, + /* ~ morekeys_swiss_row2_11 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,#,),(,/,;,\u00A1,',@,:,-,\",+,\\%,&,\u00BF", @@ -1525,7 +1522,9 @@ public final class KeyboardTextsTable { // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C", - /* morekeys_punctuation */ null, + /* keyspec_swiss_row1_11 ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_punctuation */ // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR /* morekeys_star */ "\u2605,\u066D", @@ -1553,8 +1552,7 @@ public final class KeyboardTextsTable { // U+00BF: "¿" INVERTED QUESTION MARK /* morekeys_question */ "?,\u00BF", /* morekeys_h ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_spanish_row2_10 */ // U+266A: "♪" EIGHTH NOTE /* morekeys_bullet */ "\u266A", @@ -1714,10 +1712,8 @@ public final class KeyboardTextsTable { /* morekeys_d ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ keyspec_south_slavic_row3_8 */ + null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE /* keyspec_swiss_row1_11 */ "\u00E8", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE @@ -1938,8 +1934,9 @@ public final class KeyboardTextsTable { /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ additional_morekeys_symbols_0 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, + /* ~ morekeys_swiss_row2_11 */ // U+055E: "՞" ARMENIAN QUESTION MARK // U+055C: "՜" ARMENIAN EXCLAMATION MARK // U+055A: "՚" ARMENIAN APOSTROPHE @@ -1967,8 +1964,7 @@ public final class KeyboardTextsTable { /* morekeys_h ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_greater_than */ // U+0589: "։" ARMENIAN FULL STOP /* keyspec_period */ "\u0589", @@ -2078,6 +2074,24 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00EC,\u00ED,\u00EE,\u00EF,\u012F,\u012B", + /* morekeys_c ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + /* keyspec_swiss_row1_11 */ "\u00FC", + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + /* keyspec_swiss_row2_10 */ "\u00F6", + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + /* keyspec_swiss_row2_11 */ "\u00E4", + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + /* morekeys_swiss_row1_11 */ "\u00E8", + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + /* morekeys_swiss_row2_10 */ "\u00E9", + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + /* morekeys_swiss_row2_11 */ "\u00E0", }; /* Locale iw: Hebrew */ @@ -2101,7 +2115,7 @@ public final class KeyboardTextsTable { /* morekeys_r ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_punctuation */ // U+2605: "★" BLACK STAR /* morekeys_star */ "\u2605", @@ -2130,8 +2144,7 @@ public final class KeyboardTextsTable { /* keyspec_tablet_comma ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_tablet_punctuation */ // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN @@ -2184,7 +2197,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, /* ~ morekeys_w */ // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I /* morekeys_east_slavic_row2_2 */ "\u0456", @@ -2199,8 +2213,7 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_o */ "\u04E9", /* morekeys_cyrillic_i ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ keyspec_x */ // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA /* morekeys_east_slavic_row2_11 */ "\u04BB", @@ -2265,7 +2278,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, /* ~ morekeys_east_slavic_row2_2 */ // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U /* morekeys_cyrillic_u */ "\u04AF", @@ -2504,7 +2518,8 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, /* ~ morekeys_cyrillic_o */ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE /* morekeys_cyrillic_i */ "\u045D", @@ -3045,7 +3060,8 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, /* ~ morekeys_cyrillic_o */ // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE /* morekeys_cyrillic_i */ "\u045D", @@ -3369,7 +3385,8 @@ public final class KeyboardTextsTable { /* morekeys_nordic_row2_11 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, /* ~ morekeys_w */ // U+0457: "ї" CYRILLIC SMALL LETTER YI /* morekeys_east_slavic_row2_2 */ "\u0457", @@ -3639,7 +3656,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_question */ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX /* morekeys_h */ "\u0125", @@ -3647,8 +3664,7 @@ public final class KeyboardTextsTable { /* morekeys_w */ "\u0175", /* morekeys_east_slavic_row2_2 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_v */ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX /* morekeys_j */ "\u0135", @@ -3656,7 +3672,7 @@ public final class KeyboardTextsTable { private static final Object[] LOCALES_AND_TEXTS = { // "locale", TEXT_ARRAY, /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */ - "DEFAULT", TEXTS_DEFAULT, /* 168/168 default */ + "DEFAULT", TEXTS_DEFAULT, /* 168/168 DEFAULT */ "af" , TEXTS_af, /* 7/ 12 Afrikaans */ "ar" , TEXTS_ar, /* 55/107 Arabic */ "az_AZ" , TEXTS_az_AZ, /* 8/ 17 Azerbaijani (Azerbaijan) */ @@ -3665,32 +3681,32 @@ public final class KeyboardTextsTable { "ca" , TEXTS_ca, /* 11/120 Catalan */ "cs" , TEXTS_cs, /* 17/ 21 Czech */ "da" , TEXTS_da, /* 19/ 33 Danish */ - "de" , TEXTS_de, /* 16/ 91 German */ + "de" , TEXTS_de, /* 16/ 60 German */ "el" , TEXTS_el, /* 1/ 10 Greek */ "en" , TEXTS_en, /* 8/ 11 English */ "eo" , TEXTS_eo, /* 26/115 Esperanto */ - "es" , TEXTS_es, /* 8/ 55 Spanish */ + "es" , TEXTS_es, /* 8/ 61 Spanish */ "et_EE" , TEXTS_et_EE, /* 22/ 27 Estonian (Estonia) */ "eu_ES" , TEXTS_eu_ES, /* 7/ 8 Basque (Spain) */ "fa" , TEXTS_fa, /* 58/123 Persian */ "fi" , TEXTS_fi, /* 10/ 33 Finnish */ - "fr" , TEXTS_fr, /* 13/ 91 French */ + "fr" , TEXTS_fr, /* 13/ 60 French */ "gl_ES" , TEXTS_gl_ES, /* 7/ 8 Gallegan (Spain) */ "hi" , TEXTS_hi, /* 23/ 54 Hindi */ "hr" , TEXTS_hr, /* 9/ 19 Croatian */ "hu" , TEXTS_hu, /* 9/ 19 Hungarian */ "hy_AM" , TEXTS_hy_AM, /* 8/126 Armenian (Armenia) */ "is" , TEXTS_is, /* 10/ 15 Icelandic */ - "it" , TEXTS_it, /* 5/ 5 Italian */ + "it" , TEXTS_it, /* 11/ 60 Italian */ "iw" , TEXTS_iw, /* 20/121 Hebrew */ "ka_GE" , TEXTS_ka_GE, /* 3/ 10 Georgian (Georgia) */ "kk" , TEXTS_kk, /* 15/118 Kazakh */ "km_KH" , TEXTS_km_KH, /* 2/119 Khmer (Cambodia) */ - "ky" , TEXTS_ky, /* 10/ 80 Kirghiz */ + "ky" , TEXTS_ky, /* 10/ 86 Kirghiz */ "lo_LA" , TEXTS_lo_LA, /* 2/ 20 Lao (Laos) */ "lt" , TEXTS_lt, /* 18/ 22 Lithuanian */ "lv" , TEXTS_lv, /* 18/ 22 Latvian */ - "mk" , TEXTS_mk, /* 9/ 85 Macedonian */ + "mk" , TEXTS_mk, /* 9/ 91 Macedonian */ "mn_MN" , TEXTS_mn_MN, /* 2/ 20 Mongolian (Mongolia) */ "my_MM" , TEXTS_my_MM, /* 1/ 10 Burmese (Myanmar) */ "nb" , TEXTS_nb, /* 11/ 33 Norwegian Bokmål */ @@ -3703,13 +3719,13 @@ public final class KeyboardTextsTable { "ru" , TEXTS_ru, /* 9/ 32 Russian */ "sk" , TEXTS_sk, /* 20/ 22 Slovak */ "sl" , TEXTS_sl, /* 8/ 19 Slovenian */ - "sr" , TEXTS_sr, /* 11/ 85 Serbian */ + "sr" , TEXTS_sr, /* 11/ 91 Serbian */ "sv" , TEXTS_sv, /* 21/ 33 Swedish */ "sw" , TEXTS_sw, /* 9/ 17 Swahili */ "th" , TEXTS_th, /* 2/ 20 Thai */ "tl" , TEXTS_tl, /* 7/ 8 Tagalog */ "tr" , TEXTS_tr, /* 7/ 17 Turkish */ - "uk" , TEXTS_uk, /* 11/ 79 Ukrainian */ + "uk" , TEXTS_uk, /* 11/ 85 Ukrainian */ "vi" , TEXTS_vi, /* 8/ 20 Vietnamese */ "zu" , TEXTS_zu, /* 8/ 11 Zulu */ "zz" , TEXTS_zz, /* 19/109 Alphabet */ diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 7b37777f5..5e36d9703 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -247,7 +247,9 @@ public final class BinaryDictionary extends Dictionary { final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { - if (!isValidDictionary()) return null; + if (!isValidDictionary()) { + return null; + } Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code @@ -257,12 +259,11 @@ public final class BinaryDictionary extends Dictionary { final boolean isGesture = composer.isBatchMode(); final int inputSize; if (!isGesture) { - final int composerSize = composer.sizeWithoutTrailingSingleQuotes(); - if (composerSize > MAX_WORD_LENGTH - 1) return null; - for (int i = 0; i < composerSize; i++) { - mInputCodePoints[i] = composer.getCodeAt(i); + inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + mInputCodePoints, MAX_WORD_LENGTH); + if (inputSize < 0) { + return null; } - inputSize = composerSize; } else { inputSize = inputPointers.getPointerSize(); } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 6d36af77a..0594c68cc 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -199,7 +199,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.mSettings.getCurrent()); break; case MSG_UPDATE_SHIFT_STATE: - switcher.requestUpdatingShiftState(); + switcher.requestUpdatingShiftState(latinIme.getCurrentAutoCapsState(), + latinIme.getCurrentRecapitalizeState()); break; case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) { @@ -635,6 +636,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super.onDestroy(); } + @UsedForTesting + public void recycle() { + mInputLogic.recycle(); + } + @Override public void onConfigurationChanged(final Configuration conf) { // If orientation changed while predicting, commit the change @@ -838,7 +844,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // we need to re-evaluate the shift state, but not the whole layout which would be // disruptive. // Space state must be updated before calling updateShiftState - switcher.requestUpdatingShiftState(); + switcher.requestUpdatingShiftState(getCurrentAutoCapsState(), + getCurrentRecapitalizeState()); } // This will set the punctuation suggestions if next word suggestion is off; // otherwise it will clear the suggestion strip. @@ -917,7 +924,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: find a better way to simulate actual execution. if (isInputViewShown() && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) { - mKeyboardSwitcher.requestUpdatingShiftState(); + mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), + getCurrentRecapitalizeState()); } mSubtypeState.currentSubtypeUsed(); @@ -1261,7 +1269,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: have the keyboard pass the correct key code when we need it. final Event event = Event.createSoftwareTextEvent(rawText, Event.NOT_A_KEY_CODE); mInputLogic.onTextInput(mSettings.getCurrent(), event, mHandler); - mKeyboardSwitcher.requestUpdatingShiftState(); + mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), + getCurrentRecapitalizeState()); mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT, getCurrentAutoCapsState()); } @@ -1505,7 +1514,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateShiftState(); break; case InputTransaction.SHIFT_UPDATE_NOW: - mKeyboardSwitcher.requestUpdatingShiftState(); + mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), + getCurrentRecapitalizeState()); break; default: // SHIFT_NO_UPDATE } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 81d642ff2..02f18cdd3 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -131,29 +131,42 @@ public final class WordComposer { return mCodePointSize; } - public boolean isSingleLetter() { - return size() == 1; + /** + * Copy the code points in the typed word to a destination array of ints. + * + * If the array is too small to hold the code points in the typed word, nothing is copied and + * -1 is returned. + * + * @param destination the array of ints. + * @param maxSize the size of the array. + * @return the number of copied code points. + */ + public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( + final int[] destination, final int maxSize) { + int i = mTypedWordCache.length() - 1; + while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) { + --i; + } + if (i < 0) { + // The string is empty or contains only single quotes. + return 0; + } + final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i); + if (codePointSize > maxSize) { + return -1; + } + return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0, + i + 1, true /* downCase */); } - // When the composition contains trailing quotes, we don't pass them to the suggestion engine. - // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider - // single quotes as separators because of their very common use as apostrophes. - public int sizeWithoutTrailingSingleQuotes() { - return size() - mTrailingSingleQuotesCount; + public boolean isSingleLetter() { + return size() == 1; } public final boolean isComposingWord() { return size() > 0; } - // TODO: make sure that the index should not exceed MAX_WORD_LENGTH - public int getCodeAt(int index) { - if (index >= MAX_WORD_LENGTH) { - return -1; - } - return mPrimaryKeyCodes[index]; - } - public InputPointers getInputPointers() { return mInputPointers; } diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java index e6fa1cdad..af899c040 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java +++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java @@ -30,6 +30,12 @@ public final class ProductionFlag { public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false; + // When true, enable {@link InputMethodService#onUpdateCursor} callback with + // {@link InputMethodService#setCursorAnchorMonitorMode}, which is not yet available in + // API level 19. Do not turn this on in production until the new API becomes publicly + // available. + public static final boolean USES_CURSOR_ANCHOR_MONITOR = false; + // Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}. public static final boolean INCLUDE_RAW_SUGGESTIONS = false; } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 7cf8c5e49..bf8467eb6 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -148,6 +148,17 @@ public final class InputLogic { mInputLogicHandler.reset(); } + // Normally this class just gets out of scope after the process ends, but in unit tests, we + // create several instances of LatinIME in the same process, which results in several + // instances of InputLogic. This cleans up the associated handler so that tests don't leak + // handlers. + public void recycle() { + final InputLogicHandler inputLogicHandler = mInputLogicHandler; + mInputLogicHandler = InputLogicHandler.NULL_HANDLER; + inputLogicHandler.destroy(); + mSuggest.mDictionaryFacilitator.closeDictionaries(); + } + /** * React to a string input. * @@ -537,7 +548,8 @@ public final class InputLogic { // after typing some letters and a period, then gesturing; the keyboard is not in // caps mode yet, but since a gesture is starting, it should go in caps mode, // unless the user explictly said it should not. - keyboardSwitcher.requestUpdatingShiftState(); + keyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(settingsValues), + getCurrentRecapitalizeState()); } } mConnection.endBatchEdit(); @@ -579,7 +591,8 @@ public final class InputLogic { promotePhantomSpace(settingsValues); mConnection.commitText(commitParts[0], 0); mSpaceState = SpaceState.PHANTOM; - keyboardSwitcher.requestUpdatingShiftState(); + keyboardSwitcher.requestUpdatingShiftState( + getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState()); mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), commitParts[0]); @@ -1819,7 +1832,8 @@ public final class InputLogic { } // Space state must be updated before calling updateShiftState mSpaceState = SpaceState.PHANTOM; - keyboardSwitcher.requestUpdatingShiftState(); + keyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(settingsValues), + getCurrentRecapitalizeState()); } /** diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index e3b8ab465..64bba681f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -20,6 +20,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import com.android.inputmethod.compat.LooperCompatUtils; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.Suggest; @@ -80,6 +81,12 @@ class InputLogicHandler implements Handler.Callback { mNonUIThreadHandler.removeCallbacksAndMessages(null); } + // In unit tests, we create several instances of LatinIME, which results in several instances + // of InputLogicHandler. To avoid these handlers lingering, we call this. + public void destroy() { + LooperCompatUtils.quitSafely(mNonUIThreadHandler.getLooper()); + } + /** * Handle a message. * @see android.os.Handler.Callback#handleMessage(android.os.Message) diff --git a/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java b/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java index 89837c641..1ca895fdb 100644 --- a/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtils.java @@ -18,8 +18,6 @@ package com.android.inputmethod.latin.utils; import android.view.inputmethod.InputMethodSubtype; -import java.util.Locale; - public final class SpacebarLanguageUtils { private SpacebarLanguageUtils() { // Intentional empty constructor for utility class. @@ -55,7 +53,6 @@ public final class SpacebarLanguageUtils { if (SubtypeLocaleUtils.isNoLanguage(subtype)) { return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); } - final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); - return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()); + return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(subtype.getLocale()); } } diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index accbc8b7b..374badc19 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -191,13 +191,42 @@ public final class StringUtils { } final int[] codePoints = new int[Character.codePointCount(charSequence, startIndex, endIndex)]; + copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex, + false /* downCase */); + return codePoints; + } + + /** + * Copies the codepoints in a CharSequence to an int array. + * + * This method assumes there is enough space in the array to store the code points. The size + * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this + * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown. + * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while + * this method is running, or the behavior is undefined. + * This method can optionally downcase code points before copying them, but it pays no attention + * to locale while doing so. + * + * @param destination the int array. + * @param charSequence the CharSequence. + * @param startIndex the start index inside the string in java chars, inclusive. + * @param endIndex the end index inside the string in java chars, exclusive. + * @param downCase if this is true, code points will be downcased before being copied. + * @return the number of copied code points. + */ + public static int copyCodePointsAndReturnCodePointCount(final int[] destination, + final CharSequence charSequence, final int startIndex, final int endIndex, + final boolean downCase) { int destIndex = 0; for (int index = startIndex; index < endIndex; index = Character.offsetByCodePoints(charSequence, index, 1)) { - codePoints[destIndex] = Character.codePointAt(charSequence, index); + final int codePoint = Character.codePointAt(charSequence, index); + // TODO: stop using this, as it's not aware of the locale and does not always do + // the right thing. + destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint; destIndex++; } - return codePoints; + return destIndex; } public static int[] toSortedCodePointArray(final String string) { diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 2452864d5..b37779bdc 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -25,7 +25,7 @@ import android.os.Build; import android.util.Log; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import java.util.Arrays; @@ -33,10 +33,10 @@ import java.util.HashMap; import java.util.Locale; public final class SubtypeLocaleUtils { - static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); - // This class must be located in the same package as LatinIME.java. - private static final String RESOURCE_PACKAGE_NAME = - DictionaryFactory.class.getPackage().getName(); + private static final String TAG = SubtypeLocaleUtils.class.getSimpleName(); + + // This reference class {@link Constants} must be located in the same package as LatinIME.java. + private static final String RESOURCE_PACKAGE_NAME = Constants.class.getPackage().getName(); // Special language code to represent "no language". public static final String NO_LANGUAGE = "zz"; @@ -44,7 +44,8 @@ public final class SubtypeLocaleUtils { public static final String EMOJI = "emoji"; public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic; - private static boolean sInitialized = false; + private static volatile boolean sInitialized = false; + private static final Object sInitializeLock = new Object(); private static Resources sResources; private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. @@ -77,9 +78,16 @@ public final class SubtypeLocaleUtils { } // Note that this initialization method can be called multiple times. - public static synchronized void init(final Context context) { - if (sInitialized) return; + public static void init(final Context context) { + synchronized (sInitializeLock) { + if (sInitialized == false) { + initLocked(context); + sInitialized = true; + } + } + } + private static void initLocked(final Context context) { final Resources res = context.getResources(); sResources = res; @@ -122,8 +130,6 @@ public final class SubtypeLocaleUtils { final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1]; sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet); } - - sInitialized = true; } public static String[] getPredefinedKeyboardLayoutSet() { @@ -167,8 +173,18 @@ public final class SubtypeLocaleUtils { return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale); } + public static String getSubtypeLanguageDisplayName(final String localeString) { + final Locale locale = LocaleUtils.constructLocaleFromString(localeString); + final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); + return getSubtypeLocaleDisplayNameInternal(locale.getLanguage(), displayLocale); + } + private static String getSubtypeLocaleDisplayNameInternal(final String localeString, final Locale displayLocale) { + if (NO_LANGUAGE.equals(localeString)) { + // No language subtype should be displayed in system locale. + return sResources.getString(R.string.subtype_no_language); + } final Integer exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(localeString); final String displayName; if (exceptionalNameResId != null) { @@ -179,9 +195,6 @@ public final class SubtypeLocaleUtils { } }; displayName = getExceptionalName.runInLocale(sResources, displayLocale); - } else if (NO_LANGUAGE.equals(localeString)) { - // No language subtype should be displayed in system locale. - return sResources.getString(R.string.subtype_no_language); } else { final Locale locale = LocaleUtils.constructLocaleFromString(localeString); displayName = locale.getDisplayName(displayLocale); diff --git a/native/jni/src/suggest/core/layout/proximity_info_params.cpp b/native/jni/src/suggest/core/layout/proximity_info_params.cpp index a70dd7e34..68bb0ae9d 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_params.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_params.cpp @@ -24,9 +24,6 @@ const float ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE = 1.0f; const float ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G = 0.5f; /* Per method constants */ -// Used by ProximityInfoStateUtils::initGeometricDistanceInfos() -const float ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f; - // Used by ProximityInfoStateUtils::updateNearKeysDistances() const float ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_DISTANCE = 2.0f; @@ -50,7 +47,7 @@ const int ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION = 2; const int ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE = 4; // Used by ProximityInfoStateUtils::updateAlignPointProbabilities() -const float ProximityInfoParams::MIN_PROBABILITY = 0.000001f; +const float ProximityInfoParams::MIN_PROBABILITY = 0.000005f; const float ProximityInfoParams::MAX_SKIP_PROBABILITY = 0.95f; const float ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY = 0.01f; const float ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY = 0.1f; diff --git a/native/jni/src/suggest/core/layout/proximity_info_params.h b/native/jni/src/suggest/core/layout/proximity_info_params.h index b8e9f5daf..d9515c837 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_params.h +++ b/native/jni/src/suggest/core/layout/proximity_info_params.h @@ -28,9 +28,6 @@ class ProximityInfoParams { static const float VERTICAL_SWEET_SPOT_SCALE; static const float VERTICAL_SWEET_SPOT_SCALE_G; - // Used by ProximityInfoStateUtils::initGeometricDistanceInfos() - static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD; - // Used by ProximityInfoStateUtils::updateNearKeysDistances() static const float NEAR_KEY_THRESHOLD_FOR_DISTANCE; diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.cpp b/native/jni/src/suggest/core/layout/proximity_info_state.cpp index b75c2ef67..e585f9088 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp @@ -91,7 +91,6 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi mSampledInputIndice.clear(); mSampledLengthCache.clear(); mSampledNormalizedSquaredLengthCache.clear(); - mSampledNearKeySets.clear(); mSampledSearchKeySets.clear(); mSpeedRates.clear(); mBeelineSpeedPercentiles.clear(); @@ -126,18 +125,17 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi if (mSampledInputSize > 0) { ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize, lastSavedInputSize, isGeometric, &mSampledInputXs, &mSampledInputYs, - &mSampledNearKeySets, &mSampledNormalizedSquaredLengthCache); + &mSampledNormalizedSquaredLengthCache); if (isGeometric) { // updates probabilities of skipping or mapping each key for all points. ProximityInfoStateUtils::updateAlignPointProbabilities( mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(), mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache, - &mSampledNormalizedSquaredLengthCache, &mSampledNearKeySets, - mProximityInfo, &mCharProbabilities); + &mSampledNormalizedSquaredLengthCache, mProximityInfo, &mCharProbabilities); ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo, mSampledInputSize, lastSavedInputSize, &mSampledLengthCache, - &mSampledNearKeySets, &mSampledSearchKeySets, + &mCharProbabilities, &mSampledSearchKeySets, &mSampledSearchKeyVectors); mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString( mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString); diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h index e253d9550..d66121b74 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state.h @@ -50,9 +50,9 @@ class ProximityInfoState { mSampledInputXs(), mSampledInputYs(), mSampledTimes(), mSampledInputIndice(), mSampledLengthCache(), mBeelineSpeedPercentiles(), mSampledNormalizedSquaredLengthCache(), mSpeedRates(), mDirections(), - mCharProbabilities(), mSampledNearKeySets(), mSampledSearchKeySets(), - mSampledSearchKeyVectors(), mTouchPositionCorrectionEnabled(false), - mSampledInputSize(0), mMostProbableStringProbability(0.0f) { + mCharProbabilities(), mSampledSearchKeySets(), mSampledSearchKeyVectors(), + mTouchPositionCorrectionEnabled(false), mSampledInputSize(0), + mMostProbableStringProbability(0.0f) { memset(mInputProximities, 0, sizeof(mInputProximities)); memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord)); memset(mMostProbableString, 0, sizeof(mMostProbableString)); @@ -216,10 +216,6 @@ class ProximityInfoState { std::vector<float> mDirections; // probabilities of skipping or mapping to a key for each point. std::vector<hash_map_compat<int, float> > mCharProbabilities; - // The vector for the key code set which holds nearby keys for each sampled input point - // 1. Used to calculate the probability of the key - // 2. Used to calculate mSampledSearchKeySets - std::vector<ProximityInfoStateUtils::NearKeycodesSet> mSampledNearKeySets; // The vector for the key code set which holds nearby keys of some trailing sampled input points // for each sampled input point. These nearby keys contain the next characters which can be in // the dictionary. Specifically, currently we are looking for keys nearby trailing sampled diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp index 638297eb1..72bb68fc4 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp @@ -188,13 +188,10 @@ namespace latinime { const int lastSavedInputSize, const bool isGeometric, const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs, - std::vector<NearKeycodesSet> *sampledNearKeySets, std::vector<float> *sampledNormalizedSquaredLengthCache) { - sampledNearKeySets->resize(sampledInputSize); const int keyCount = proximityInfo->getKeyCount(); sampledNormalizedSquaredLengthCache->resize(sampledInputSize * keyCount); for (int i = lastSavedInputSize; i < sampledInputSize; ++i) { - (*sampledNearKeySets)[i].reset(); for (int k = 0; k < keyCount; ++k) { const int index = i * keyCount + k; const int x = (*sampledInputXs)[i]; @@ -203,10 +200,6 @@ namespace latinime { proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG( k, x, y, isGeometric); (*sampledNormalizedSquaredLengthCache)[index] = normalizedSquaredDistance; - if (normalizedSquaredDistance - < ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) { - (*sampledNearKeySets)[i][k] = true; - } } } } @@ -626,7 +619,6 @@ namespace latinime { const std::vector<float> *const sampledSpeedRates, const std::vector<int> *const sampledLengthCache, const std::vector<float> *const sampledNormalizedSquaredLengthCache, - std::vector<NearKeycodesSet> *sampledNearKeySets, const ProximityInfo *const proximityInfo, std::vector<hash_map_compat<int, float> > *charProbabilities) { charProbabilities->resize(sampledInputSize); @@ -643,12 +635,10 @@ namespace latinime { float nearestKeyDistance = static_cast<float>(MAX_VALUE_FOR_WEIGHTING); for (int j = 0; j < keyCount; ++j) { - if ((*sampledNearKeySets)[i].test(j)) { - const float distance = getPointToKeyByIdLength( - maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j); - if (distance < nearestKeyDistance) { - nearestKeyDistance = distance; - } + const float distance = getPointToKeyByIdLength( + maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j); + if (distance < nearestKeyDistance) { + nearestKeyDistance = distance; } } @@ -744,27 +734,23 @@ namespace latinime { // Summing up probability densities of all near keys. float sumOfProbabilityDensities = 0.0f; for (int j = 0; j < keyCount; ++j) { - if ((*sampledNearKeySets)[i].test(j)) { - sumOfProbabilityDensities += distribution.getProbabilityDensity( - proximityInfo->getKeyCenterXOfKeyIdG(j, - NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */), - proximityInfo->getKeyCenterYOfKeyIdG(j, - NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */)); - } + sumOfProbabilityDensities += distribution.getProbabilityDensity( + proximityInfo->getKeyCenterXOfKeyIdG(j, + NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */), + proximityInfo->getKeyCenterYOfKeyIdG(j, + NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */)); } // Split the probability of an input point to keys that are close to the input point. for (int j = 0; j < keyCount; ++j) { - if ((*sampledNearKeySets)[i].test(j)) { - const float probabilityDensity = distribution.getProbabilityDensity( - proximityInfo->getKeyCenterXOfKeyIdG(j, - NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */), - proximityInfo->getKeyCenterYOfKeyIdG(j, - NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */)); - const float probability = inputCharProbability * probabilityDensity - / sumOfProbabilityDensities; - (*charProbabilities)[i][j] = probability; - } + const float probabilityDensity = distribution.getProbabilityDensity( + proximityInfo->getKeyCenterXOfKeyIdG(j, + NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */), + proximityInfo->getKeyCenterYOfKeyIdG(j, + NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */)); + const float probability = inputCharProbability * probabilityDensity + / sumOfProbabilityDensities; + (*charProbabilities)[i][j] = probability; } } @@ -820,10 +806,9 @@ namespace latinime { for (int j = 0; j < keyCount; ++j) { hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j); if (it == (*charProbabilities)[i].end()){ - (*sampledNearKeySets)[i].reset(j); + continue; } else if(it->second < ProximityInfoParams::MIN_PROBABILITY) { // Erases from near keys vector because it has very low probability. - (*sampledNearKeySets)[i].reset(j); (*charProbabilities)[i].erase(j); } else { it->second = -logf(it->second); @@ -835,9 +820,8 @@ namespace latinime { /* static */ void ProximityInfoStateUtils::updateSampledSearchKeySets( const ProximityInfo *const proximityInfo, const int sampledInputSize, - const int lastSavedInputSize, - const std::vector<int> *const sampledLengthCache, - const std::vector<NearKeycodesSet> *const sampledNearKeySets, + const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache, + const std::vector<hash_map_compat<int, float> > *const charProbabilities, std::vector<NearKeycodesSet> *sampledSearchKeySets, std::vector<std::vector<int> > *sampledSearchKeyVectors) { sampledSearchKeySets->resize(sampledInputSize); @@ -854,7 +838,12 @@ namespace latinime { if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) { break; } - (*sampledSearchKeySets)[i] |= (*sampledNearKeySets)[j]; + for(const auto& charProbability : charProbabilities->at(j)) { + if (charProbability.first == NOT_AN_INDEX) { + continue; + } + (*sampledSearchKeySets)[i].set(charProbability.first); + } } } const int keyCount = proximityInfo->getKeyCount(); diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h index 5d7a9c589..7aa20c3d1 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h @@ -71,13 +71,12 @@ class ProximityInfoStateUtils { const std::vector<float> *const sampledSpeedRates, const std::vector<int> *const sampledLengthCache, const std::vector<float> *const sampledNormalizedSquaredLengthCache, - std::vector<NearKeycodesSet> *sampledNearKeySets, const ProximityInfo *const proximityInfo, std::vector<hash_map_compat<int, float> > *charProbabilities); static void updateSampledSearchKeySets(const ProximityInfo *const proximityInfo, const int sampledInputSize, const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache, - const std::vector<NearKeycodesSet> *const sampledNearKeySets, + const std::vector<hash_map_compat<int, float> > *const charProbabilities, std::vector<NearKeycodesSet> *sampledSearchKeySets, std::vector<std::vector<int> > *sampledSearchKeyVectors); static float getPointToKeyByIdLength(const float maxPointToKeyLength, @@ -87,7 +86,6 @@ class ProximityInfoStateUtils { const int sampledInputSize, const int lastSavedInputSize, const bool isGeometric, const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs, - std::vector<NearKeycodesSet> *sampledNearKeySets, std::vector<float> *sampledNormalizedSquaredLengthCache); static void initPrimaryInputWord(const int inputSize, const int *const inputProximities, int *primaryInputWord); diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h index d3b8da0cc..8ddaa0514 100644 --- a/native/jni/src/suggest/core/policy/traversal.h +++ b/native/jni/src/suggest/core/policy/traversal.h @@ -45,6 +45,7 @@ class Traversal { virtual float getMaxSpatialDistance() const = 0; virtual int getDefaultExpandDicNodeSize() const = 0; virtual int getMaxCacheSize(const int inputSize) const = 0; + virtual int getTerminalCacheSize() const = 0; virtual bool isPossibleOmissionChildNode(const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0; virtual bool isGoodToTraverseNextWord(const DicNode *const dicNode) const = 0; diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 433820a42..e675e0bb3 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -88,7 +88,7 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession) const { } else { // Restart recognition at the root. traverseSession->resetCache(TRAVERSAL->getMaxCacheSize(traverseSession->getInputSize()), - MAX_RESULTS); + TRAVERSAL->getTerminalCacheSize()); // Create a new dic node here DicNode rootNode; DicNodeUtils::initAsRoot(traverseSession->getDictionaryStructurePolicy(), diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index 5ba8bfa01..cb3dfac70 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -146,6 +146,10 @@ class TypingTraversal : public Traversal { : ScoringParams::MAX_CACHE_DIC_NODE_SIZE; } + AK_FORCE_INLINE int getTerminalCacheSize() const { + return MAX_RESULTS; + } + AK_FORCE_INLINE bool isPossibleOmissionChildNode( const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const { diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java index 57d295058..e4aaf0317 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java @@ -25,8 +25,8 @@ import java.util.ArrayList; @SmallTest public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase { - private static final int NUMBER_OF_SUBTYPES = 68; - private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 43; + private static final int NUMBER_OF_SUBTYPES = 70; + private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 45; private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2; private static String toString(final ArrayList<InputMethodSubtype> subtypeList) { diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java index 0ec9f4cf4..475052c75 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java @@ -85,7 +85,7 @@ public final class Greek extends LayoutBase { key(ROW1_2, moreKey("2")), // U+03B5: "ε" GREEK SMALL LETTER EPSILON // U+03AD: "έ" GREEK SMALL LETTER EPSILON WITH TONOS - key("\u03B5", joinMoreKeys("3", "\u03AD")), + key("\u03B5", joinMoreKeys("\u03AD", "3")), // U+03C1: "ρ" GREEK SMALL LETTER RHO key("\u03C1", moreKey("4")), // U+03C4: "τ" GREEK SMALL LETTER TAU @@ -94,17 +94,17 @@ public final class Greek extends LayoutBase { // U+03CD: "ύ" GREEK SMALL LETTER UPSILON WITH TONOS // U+03CB: "ϋ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS - key("\u03C5", joinMoreKeys("6", "\u03CD", "\u03CB", "\u03B0")), + key("\u03C5", joinMoreKeys("\u03CD", "6", "\u03CB", "\u03B0")), // U+03B8: "θ" GREEK SMALL LETTER THETA key("\u03B8", moreKey("7")), // U+03B9: "ι" GREEK SMALL LETTER IOTA // U+03AF: "ί" GREEK SMALL LETTER IOTA WITH TONOS // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA // U+0390: "ΐ" GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS - key("\u03B9", joinMoreKeys("8", "\u03AF", "\u03CA", "\u0390")), + key("\u03B9", joinMoreKeys("\u03AF", "8", "\u03CA", "\u0390")), // U+03BF: "ο" GREEK SMALL LETTER OMICRON // U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS - key("\u03BF", joinMoreKeys("9", "\u03CC")), + key("\u03BF", joinMoreKeys("\u03CC", "9")), // U+03C0: "π" GREEK SMALL LETTER PI key("\u03C0", moreKey("0"))) .setKeysOfRow(2, diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java index cd881406e..6d38937aa 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java @@ -53,6 +53,7 @@ class GermanCustomizer extends LayoutCustomizer { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON .setMoreKeysOf("u", "\u00FC", "\u00FB", "\u00F9", "\u00FA", "\u016B") + .setAdditionalMoreKeysPositionOf("u", 2) // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE @@ -64,6 +65,7 @@ class GermanCustomizer extends LayoutCustomizer { .setMoreKeysOf("o", "\u00F6", "\u00F4", "\u00F2", "\u00F3", "\u00F5", "\u0153", "\u00F8", "\u014D") + .setAdditionalMoreKeysPositionOf("o", 2) // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -75,6 +77,7 @@ class GermanCustomizer extends LayoutCustomizer { .setMoreKeysOf("a", "\u00E4", "\u00E2", "\u00E0", "\u00E1", "\u00E6", "\u00E3", "\u00E5", "\u0101") + .setAdditionalMoreKeysPositionOf("a", 2) // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java new file mode 100644 index 000000000..735070946 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; + +import java.util.Locale; + +class ItalianCustomizer extends LayoutCustomizer { + public ItalianCustomizer(final Locale locale) { super(locale); } + + @Override + public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) { + return builder + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX + // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS + // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK + // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE + // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON + .setMoreKeysOf("e", + "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113") + // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + .setMoreKeysOf("u", "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u016B") + // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS + // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK + // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON + .setMoreKeysOf("i", "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u012F", "\u012B") + // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE + // U+0153: "œ" LATIN SMALL LIGATURE OE + // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE + // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON + // U+00BA: "º" MASCULINE ORDINAL INDICATOR + .setMoreKeysOf("o", + "\u00F2", "\u00F3", "\u00F4", "\u00F6", "\u00F5", "\u0153", "\u00F8", + "\u014D", "\u00BA") + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE + // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E6: "æ" LATIN SMALL LETTER AE + // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE + // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE + // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON + // U+00AA: "ª" FEMININE ORDINAL INDICATOR + .setMoreKeysOf("a", + "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5", + "\u0101", "\u00AA"); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java index 4a2200364..f3c610c8b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java @@ -31,63 +31,22 @@ import java.util.Locale; @SmallTest public final class TestsItalian extends LayoutTestsBase { private static final Locale LOCALE = new Locale("it"); - private static final LayoutBase LAYOUT = new Qwerty(new ItalianCustomizer(LOCALE)); + private static final LayoutBase LAYOUT = new Qwerty(new ItalianITCustomizer(LOCALE)); @Override LayoutBase getLayout() { return LAYOUT; } - private static class ItalianCustomizer extends EuroCustomizer { - public ItalianCustomizer(final Locale locale) { super(locale); } + private static class ItalianITCustomizer extends EuroCustomizer { + private final ItalianCustomizer mItalianCustomizer; + + public ItalianITCustomizer(final Locale locale) { + super(locale); + mItalianCustomizer = new ItalianCustomizer(locale); + } @Override public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) { - return builder - // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE - // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE - // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX - // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS - // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK - // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE - // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON - .setMoreKeysOf("e", - "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113") - // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE - // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE - // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX - // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS - // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - .setMoreKeysOf("u", "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u016B") - // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE - // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE - // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX - // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS - // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK - // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON - .setMoreKeysOf("i", "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u012F", "\u012B") - // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE - // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE - // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX - // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS - // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE - // U+0153: "œ" LATIN SMALL LIGATURE OE - // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE - // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON - // U+00BA: "º" MASCULINE ORDINAL INDICATOR - .setMoreKeysOf("o", - "\u00F2", "\u00F3", "\u00F4", "\u00F6", "\u00F5", "\u0153", "\u00F8", - "\u014D", "\u00BA") - // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE - // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE - // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX - // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS - // U+00E6: "æ" LATIN SMALL LETTER AE - // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE - // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE - // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON - // U+00AA: "ª" FEMININE ORDINAL INDICATOR - .setMoreKeysOf("a", - "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5", - "\u0101", "\u00AA"); + return mItalianCustomizer.setAccentedLetters(builder); } } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java new file mode 100644 index 000000000..d32f9e957 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Swiss; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; + +import java.util.Locale; + +/** + * it_CH: Italian (Switzerland)/swiss + */ +@SmallTest +public final class TestsItalianCH extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("it", "CH"); + private static final LayoutBase LAYOUT = new Swiss(new ItalianCHCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } + + private static class ItalianCHCustomizer extends ItalianCustomizer { + public ItalianCHCustomizer(final Locale locale) { super(locale); } + + @Override + public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) { + super.setAccentedLetters(builder); + return builder + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + .replaceKeyOfLabel(Swiss.ROW1_11, key("\u00FC", moreKey("\u00E8"))) + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + .replaceKeyOfLabel(Swiss.ROW2_10, key("\u00F6", moreKey("\u00E9"))) + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + .replaceKeyOfLabel(Swiss.ROW2_11, key("\u00E4", moreKey("\u00E0"))); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java new file mode 100644 index 000000000..75aad136f --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Spanish; + +import java.util.Locale; + +/** + * es_419: Spanish (Latin America)/spanish + */ +@SmallTest +public class TestsSpanish419 extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("es", "419"); + private static final LayoutBase LAYOUT = new Spanish(new SpanishCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } +} diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 1383ff903..e5f111ab6 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -213,13 +213,18 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { @Override protected void tearDown() throws Exception { + mLatinIME.onFinishInputView(true); + mLatinIME.onFinishInput(); + runMessages(); mLatinIME.mHandler.removeAllMessages(); setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mPreviousBigramPredictionSettings, true /* defaultValue */); setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, mPreviousAutoCorrectSetting, DEFAULT_AUTO_CORRECTION_THRESHOLD); setDebugMode(false); + mLatinIME.recycle(); super.tearDown(); + mLatinIME = null; } // We need to run the messages added to the handler from LatinIME. The only way to do diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java index e55c32bd0..2a4ead383 100644 --- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java @@ -308,4 +308,68 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertEquals(objs[i], newObjArray.get(i)); } } + + public void testToCodePointArray() { + final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx"; + final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h', + 0, 0x2002, 0x2003, 0x3000, 'x', 'x'}; + final int[] codePointArray = StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR, 0, + STR_WITH_SUPPLEMENTARY_CHAR.length()); + assertEquals("toCodePointArray, size matches", codePointArray.length, + EXPECTED_RESULT.length); + for (int i = 0; i < EXPECTED_RESULT.length; ++i) { + assertEquals("toCodePointArray position " + i, codePointArray[i], EXPECTED_RESULT[i]); + } + } + + public void testCopyCodePointsAndReturnCodePointCount() { + final String STR_WITH_SUPPLEMENTARY_CHAR = "AbcDE\uD861\uDED7fGh\u0000\u2002\u3000あx"; + final int[] EXPECTED_RESULT = new int[] { 'A', 'b', 'c', 'D', 'E', 0x286D7, + 'f', 'G', 'h', 0, 0x2002, 0x3000, 'あ', 'x'}; + final int[] EXPECTED_RESULT_DOWNCASE = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, + 'f', 'g', 'h', 0, 0x2002, 0x3000, 'あ', 'x'}; + + int[] codePointArray = new int[50]; + int codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, + STR_WITH_SUPPLEMENTARY_CHAR, 0, + STR_WITH_SUPPLEMENTARY_CHAR.length(), false /* downCase */); + assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount, + EXPECTED_RESULT.length); + for (int i = 0; i < codePointCount; ++i) { + assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], + EXPECTED_RESULT[i]); + } + + codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, + STR_WITH_SUPPLEMENTARY_CHAR, 0, + STR_WITH_SUPPLEMENTARY_CHAR.length(), true /* downCase */); + assertEquals("copyCodePointsAndReturnCodePointCount downcase, size matches", codePointCount, + EXPECTED_RESULT_DOWNCASE.length); + for (int i = 0; i < codePointCount; ++i) { + assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], + EXPECTED_RESULT_DOWNCASE[i]); + } + + final int JAVA_CHAR_COUNT = 8; + final int CODEPOINT_COUNT = 7; + codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, + STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */); + assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount, + CODEPOINT_COUNT); + for (int i = 0; i < codePointCount; ++i) { + assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], + EXPECTED_RESULT[i]); + } + + boolean exceptionHappened = false; + codePointArray = new int[5]; + try { + codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, + STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */); + } catch (ArrayIndexOutOfBoundsException e) { + exceptionHappened = true; + } + assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small", + exceptionHappened); + } } diff --git a/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml index 94b17243c..0c6d3ad2b 100644 --- a/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml @@ -26,7 +26,7 @@ U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE U+0101: "ā" LATIN SMALL LETTER A WITH MACRON --> - <string name="morekeys_a">ä,â,à,á,æ,ã,å,ā</string> + <string name="morekeys_a">ä,%,â,à,á,æ,ã,å,ā</string> <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -41,13 +41,13 @@ U+0153: "œ" LATIN SMALL LIGATURE OE U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE U+014D: "ō" LATIN SMALL LETTER O WITH MACRON --> - <string name="morekeys_o">ö,ô,ò,ó,õ,œ,ø,ō</string> + <string name="morekeys_o">ö,%,ô,ò,ó,õ,œ,ø,ō</string> <!-- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE U+016B: "ū" LATIN SMALL LETTER U WITH MACRON --> - <string name="morekeys_u">ü,û,ù,ú,ū</string> + <string name="morekeys_u">ü,%,û,ù,ú,ū</string> <!-- U+00DF: "ß" LATIN SMALL LETTER SHARP S U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE U+0161: "š" LATIN SMALL LETTER S WITH CARON --> diff --git a/tools/make-keyboard-text/res/values-it/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-it/donottranslate-more-keys.xml index 6492553ed..e809f4835 100644 --- a/tools/make-keyboard-text/res/values-it/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-it/donottranslate-more-keys.xml @@ -59,4 +59,16 @@ U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS U+016B: "ū" LATIN SMALL LETTER U WITH MACRON --> <string name="morekeys_u">ù,ú,û,ü,ū</string> + <!-- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS --> + <string name="keyspec_swiss_row1_11">ü</string> + <!-- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE --> + <string name="morekeys_swiss_row1_11">è</string> + <!-- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS --> + <string name="keyspec_swiss_row2_10">ö</string> + <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE --> + <string name="morekeys_swiss_row2_10">é</string> + <!-- U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS --> + <string name="keyspec_swiss_row2_11">ä</string> + <!-- U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE --> + <string name="morekeys_swiss_row2_11">à</string> </resources> diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java index b892df236..c947a63bf 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java @@ -24,6 +24,7 @@ import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; +import java.util.Locale; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -86,23 +87,23 @@ public final class JarUtils { } // The locale is taken from string resource jar entry name (values-<locale>/) - // or {@link LocaleUtils#DEFAULT_LOCALE_KEY} for the default string resource + // or {@link LocaleUtils#DEFAULT_LOCALE} for the default string resource // directory (values/). - public static String getLocaleFromEntryName(final String jarEntryName) { + public static Locale getLocaleFromEntryName(final String jarEntryName) { final String dirName = jarEntryName.substring(0, jarEntryName.lastIndexOf('/')); final int pos = dirName.lastIndexOf('/'); final String parentName = (pos >= 0) ? dirName.substring(pos + 1) : dirName; final int localePos = parentName.indexOf('-'); if (localePos < 0) { // Default resource name. - return LocaleUtils.DEFAULT_LOCALE_KEY; + return LocaleUtils.DEFAULT_LOCALE; } - final String locale = parentName.substring(localePos + 1); - final int regionPos = locale.indexOf("-r"); + final String localeStr = parentName.substring(localePos + 1); + final int regionPos = localeStr.indexOf("-r"); if (regionPos < 0) { - return locale; + return LocaleUtils.constructLocaleFromString(localeStr); } - return locale.replace("-r", "_"); + return LocaleUtils.constructLocaleFromString(localeStr.replace("-r", "_")); } public static void close(final Closeable stream) { diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java index d0f8b4292..0dfa37667 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java @@ -26,7 +26,8 @@ import java.util.Locale; * for the make-keyboard-text tool. */ public final class LocaleUtils { - public static final String DEFAULT_LOCALE_KEY = "DEFAULT"; + public static final Locale DEFAULT_LOCALE = Locale.ROOT; + private static final String DEFAULT_LOCALE_CODE = "DEFAULT"; public static final String NO_LANGUAGE_LOCALE_CODE = "zz"; public static final String NO_LANGUAGE_LOCALE_DISPLAY_NAME = "Alphabet"; @@ -36,39 +37,131 @@ public final class LocaleUtils { private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>(); + private static final int INDEX_LANGUAGE = 0; + private static final int INDEX_SCRIPT = 1; + private static final int INDEX_REGION = 2; + private static final int ELEMENT_LIMIT = INDEX_REGION + 1; + /** * Creates a locale from a string specification. + * + * Locale string is: language(_script)?(_region)? + * where: language := [a-zA-Z]{2,3} + * script := [a-zA-Z]{4} + * region := [a-zA-Z]{2,3}|[0-9]{3} */ public static Locale constructLocaleFromString(final String localeStr) { if (localeStr == null) { return null; } synchronized (sLocaleCache) { - Locale retval = sLocaleCache.get(localeStr); - if (retval != null) { - return retval; + if (sLocaleCache.containsKey(localeStr)) { + return sLocaleCache.get(localeStr); + } + boolean hasRegion = false; + final Locale.Builder builder = new Locale.Builder(); + final String[] localeElements = localeStr.split("_", ELEMENT_LIMIT); + if (localeElements.length > INDEX_LANGUAGE) { + final String text = localeElements[INDEX_LANGUAGE]; + if (isValidLanguage(text)) { + builder.setLanguage(text); + } else { + throw new RuntimeException("Unknown locale format: " + localeStr); + } } - final String[] localeParams = localeStr.split("_", 3); - // TODO: Use JDK 7 Locale.Builder to handle a script name. - if (localeParams.length == 1) { - retval = new Locale(localeParams[0]); - } else if (localeParams.length == 2) { - retval = new Locale(localeParams[0], localeParams[1]); - } else if (localeParams.length == 3) { - retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); + if (localeElements.length > INDEX_SCRIPT) { + final String text = localeElements[INDEX_SCRIPT]; + if (isValidScript(text)) { + builder.setScript(text); + } else if (isValidRegion(text)) { + builder.setRegion(text); + hasRegion = true; + } else { + throw new RuntimeException("Unknown locale format: " + localeStr); + } } - if (retval != null) { - sLocaleCache.put(localeStr, retval); + if (localeElements.length > INDEX_REGION) { + final String text = localeElements[INDEX_REGION]; + if (!hasRegion && isValidRegion(text)) { + builder.setRegion(text); + } else { + throw new RuntimeException("Unknown locale format: " + localeStr); + } + } + final Locale locale = builder.build(); + sLocaleCache.put(localeStr, locale); + return locale; + } + } + + private static final int MIN_LENGTH_OF_LANGUAGE = 2; + private static final int MAX_LENGTH_OF_LANGUAGE = 2; + private static final int LENGTH_OF_SCRIPT = 4; + private static final int MIN_LENGTH_OF_REGION = 2; + private static final int MAX_LENGTH_OF_REGION = 2; + private static final int LENGTH_OF_AREA_CODE = 3; + + private static boolean isValidLanguage(final String text) { + return isAlphabetSequence(text, MIN_LENGTH_OF_LANGUAGE, MAX_LENGTH_OF_LANGUAGE); + } + + private static boolean isValidScript(final String text) { + return isAlphabetSequence(text, LENGTH_OF_SCRIPT, LENGTH_OF_SCRIPT); + } + + private static boolean isValidRegion(final String text) { + return isAlphabetSequence(text, MIN_LENGTH_OF_REGION, MAX_LENGTH_OF_REGION) + || isDigitSequence(text, LENGTH_OF_AREA_CODE, LENGTH_OF_AREA_CODE); + } + + private static boolean isAlphabetSequence(final String text, final int lower, final int upper) { + final int length = text.length(); + if (length < lower || length > upper) { + return false; + } + for (int index = 0; index < length; index++) { + if (!isAsciiAlphabet(text.charAt(index))) { + return false; } - return retval; } + return true; } - public static String getLocaleDisplayName(final String localeString) { - if (localeString.equals(NO_LANGUAGE_LOCALE_CODE)) { + private static boolean isDigitSequence(final String text, final int lower, final int upper) { + final int length = text.length(); + if (length < lower || length > upper) { + return false; + } + for (int index = 0; index < length; ++index) { + if (!isAsciiDigit(text.charAt(index))) { + return false; + } + } + return true; + } + + private static boolean isAsciiAlphabet(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + private static boolean isAsciiDigit(char c) { + return c >= '0' && c <= '9'; + } + + public static String getLocaleCode(final Locale locale) { + if (locale == DEFAULT_LOCALE) { + return DEFAULT_LOCALE_CODE; + } + return locale.toString(); + } + + public static String getLocaleDisplayName(final Locale locale) { + if (locale == DEFAULT_LOCALE) { + return DEFAULT_LOCALE_CODE; + } + if (locale.getLanguage().equals(NO_LANGUAGE_LOCALE_CODE)) { return NO_LANGUAGE_LOCALE_DISPLAY_NAME; } - final Locale locale = constructLocaleFromString(localeString); return locale.getDisplayName(Locale.ENGLISH); } } diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java index c1a9753cc..c8cb4acec 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Locale; import java.util.TreeMap; import java.util.jar.JarFile; @@ -60,9 +61,10 @@ public class MoreKeysResources { jar, TEXT_RESOURCE_NAME); for (final String entryName : resourceEntryNames) { final StringResourceMap resMap = new StringResourceMap(entryName); - mResourcesMap.put(resMap.mLocale, resMap); + mResourcesMap.put(LocaleUtils.getLocaleCode(resMap.mLocale), resMap); } - mDefaultResourceMap = mResourcesMap.get(LocaleUtils.DEFAULT_LOCALE_KEY); + mDefaultResourceMap = mResourcesMap.get( + LocaleUtils.getLocaleCode(LocaleUtils.DEFAULT_LOCALE)); // Initialize name histogram and names list. final HashMap<String, Integer> nameHistogram = mNameHistogram; @@ -165,13 +167,13 @@ public class MoreKeysResources { mDefaultResourceMap.setOutputArraySize(outputArraySize); } - private static String getArrayNameForLocale(final String locale) { - return TEXTS_ARRAY_NAME_PREFIX + locale; + private static String getArrayNameForLocale(final Locale locale) { + return TEXTS_ARRAY_NAME_PREFIX + LocaleUtils.getLocaleCode(locale); } private void dumpTexts(final PrintStream out) { for (final StringResourceMap resMap : mResourcesMap.values()) { - final String locale = resMap.mLocale; + final Locale locale = resMap.mLocale; if (resMap == mDefaultResourceMap) continue; out.format(" /* Locale %s: %s */\n", locale, LocaleUtils.getLocaleDisplayName(locale)); @@ -185,10 +187,11 @@ public class MoreKeysResources { private void dumpLocalesMap(final PrintStream out) { for (final StringResourceMap resMap : mResourcesMap.values()) { - final String locale = resMap.mLocale; - final String localeToDump = locale.equals(LocaleUtils.DEFAULT_LOCALE_KEY) - ? String.format("\"%s\"", locale) - : String.format("\"%s\"%s", locale, " ".substring(locale.length())); + final Locale locale = resMap.mLocale; + final String localeStr = LocaleUtils.getLocaleCode(locale); + final String localeToDump = (locale == LocaleUtils.DEFAULT_LOCALE) + ? String.format("\"%s\"", localeStr) + : String.format("\"%s\"%s", localeStr, " ".substring(localeStr.length())); out.format(" %s, %-12s /* %3d/%3d %s */\n", localeToDump, getArrayNameForLocale(locale) + ",", resMap.getResources().size(), resMap.getOutputArraySize(), diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java index d7e76ad77..6a79268e5 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; @@ -34,8 +35,8 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; public class StringResourceMap { - // Locale name. - public final String mLocale; + // Locale of this string resource map. + public final Locale mLocale; // String resource list. private final List<StringResource> mResources; // Name to string resource map. |