diff options
Diffstat (limited to 'java/src')
19 files changed, 521 insertions, 365 deletions
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index 4a9163c8e..d257441e0 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -225,6 +225,13 @@ public class Event { null /* suggestedWordInfo */, FLAG_NONE, null); } + // Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys + // that result in input like letters or space. + public boolean isFunctionalKeyEvent() { + // This logic may need to be refined in the future + return NOT_A_CODE_POINT == mCodePoint; + } + // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD} public boolean isDead() { return 0 != (FLAG_DEAD & mFlags); diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java index 05ba99923..c61f45efa 100644 --- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java +++ b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java @@ -74,7 +74,7 @@ public class HardwareKeyboardEventDecoder implements HardwareEventDecoder { } // If not Enter, then this is just a regular keypress event for a normal character // that can be committed right away, taking into account the current state. - return Event.createHardwareKeypressEvent(keyCode, codePointAndFlags, null /* next */, + return Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */, isKeyRepeat); } return Event.createNotHandledEvent(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index ab2555802..13b174e94 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -89,9 +89,9 @@ public final class KeyboardTextsTable { /* 3:29 */ "morekeys_e", /* 4:28 */ "morekeys_i", /* 5:23 */ "morekeys_c", - /* 6:23 */ "double_quotes", - /* 7:22 */ "morekeys_n", - /* 8:22 */ "keylabel_to_alpha", + /* 6:23 */ "keylabel_to_alpha", + /* 7:23 */ "double_quotes", + /* 8:22 */ "morekeys_n", /* 9:22 */ "single_quotes", /* 10:20 */ "morekeys_s", /* 11:14 */ "morekeys_y", @@ -260,10 +260,10 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_lqm_rqm", - /* morekeys_n */ EMPTY, // Label for "switch to alphabetic" key. /* keylabel_to_alpha */ "ABC", + /* double_quotes */ "!text/double_lqm_rqm", + /* morekeys_n */ EMPTY, /* single_quotes */ "!text/single_lqm_rqm", /* morekeys_s ~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, @@ -507,14 +507,14 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_i */ "\u00ED,\u00EC,\u00EF,\u00EE,\u012F,\u012B,\u0133", - /* morekeys_c */ null, - /* double_quotes */ null, + /* morekeys_c ~ */ + null, null, null, + /* ~ double_quotes */ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha ~ */ - null, null, null, - /* ~ morekeys_s */ + /* single_quotes */ null, + /* morekeys_s */ null, // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_y */ "\u00FD,\u0133", @@ -523,17 +523,17 @@ public final class KeyboardTextsTable { /* Locale ar: Arabic */ private static final String[] TEXTS_ar = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "ج" ARABIC LETTER JEEM /* keylabel_to_alpha */ "\u0623\u200C\u0628\u200C\u062C", - /* single_quotes ~ */ + /* double_quotes ~ */ null, null, null, null, null, null, null, null, null, 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_soft_sign */ // U+0661: "١" ARABIC-INDIC DIGIT ONE /* keyspec_symbols_1 */ "\u0661", @@ -685,7 +685,7 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", - /* double_quotes ~ */ + /* keylabel_to_alpha ~ */ null, null, null, null, /* ~ single_quotes */ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA @@ -705,13 +705,13 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* double_quotes */ "!text/double_9qm_lqm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, @@ -738,14 +738,13 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - // single_quotes of Bulgarian is default single_quotes_right_left. - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + // single_quotes of Bulgarian is default single_quotes_right_left. + /* double_quotes */ "!text/double_9qm_lqm", }; /* Locale ca: Catalan */ @@ -796,12 +795,13 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha ~ */ - null, null, null, null, null, null, null, + /* single_quotes ~ */ + null, null, null, null, null, null, /* ~ morekeys_t */ // U+00B7: "·" MIDDLE DOT // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE @@ -870,12 +870,12 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u00E7,\u0107", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+0148: "ň" LATIN SMALL LETTER N WITH CARON // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0148,\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -930,11 +930,11 @@ public final class KeyboardTextsTable { // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS /* morekeys_i */ "\u00ED,\u00EF", /* morekeys_c */ null, + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -1004,13 +1004,13 @@ public final class KeyboardTextsTable { // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0117", - /* morekeys_i */ null, - /* morekeys_c */ null, + /* morekeys_i ~ */ + null, null, null, + /* ~ keylabel_to_alpha */ /* double_quotes */ "!text/double_9qm_lqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -1043,8 +1043,8 @@ public final class KeyboardTextsTable { /* Locale el: Greek */ private static final String[] TEXTS_el = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA @@ -1092,10 +1092,10 @@ public final class KeyboardTextsTable { /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC", // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u00F1", - /* keylabel_to_alpha */ null, /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S /* morekeys_s */ "\u00DF", @@ -1161,6 +1161,7 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE /* morekeys_c */ "\u0107,\u010D,\u00E7,\u010B", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -1169,7 +1170,6 @@ public final class KeyboardTextsTable { // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE // U+014B: "ŋ" LATIN SMALL LETTER ENG /* morekeys_n */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B", - /* keylabel_to_alpha */ null, /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+0161: "š" LATIN SMALL LETTER S WITH CARON @@ -1297,15 +1297,15 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha ~ */ + /* single_quotes ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 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_nordic_row2_11 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK @@ -1363,12 +1363,12 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u00E7,\u0107", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0146,\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -1463,6 +1463,7 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -1472,16 +1473,16 @@ public final class KeyboardTextsTable { /* Locale fa: Persian */ private static final String[] TEXTS_fa = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0627: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+067E: "پ" ARABIC LETTER PEH /* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+FDFC: "﷼" RIAL SIGN /* keyspec_currency */ "\uFDFC", @@ -1710,7 +1711,7 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,%,\u0107,\u010D", - /* double_quotes ~ */ + /* keylabel_to_alpha ~ */ null, null, null, null, null, /* ~ morekeys_s */ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS @@ -1782,6 +1783,7 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -1791,15 +1793,15 @@ public final class KeyboardTextsTable { /* Locale hi: Hindi */ private static final String[] TEXTS_hi = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -1849,11 +1851,11 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u010D,\u0107,\u00E7", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_rqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -1917,9 +1919,9 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u00EC,\u012F,\u012B", /* morekeys_c */ null, + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, @@ -1931,17 +1933,18 @@ public final class KeyboardTextsTable { /* Locale hy_AM: Armenian (Armenia) */ private static final String[] TEXTS_hy_AM = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0531: "Ա" ARMENIAN CAPITAL LETTER AYB // U+0532: "Բ" ARMENIAN CAPITAL LETTER BEN // U+0533: "Գ" ARMENIAN CAPITAL LETTER GIM /* keylabel_to_alpha */ "\u0531\u0532\u0533", - /* single_quotes ~ */ + /* double_quotes ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 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_nordic_row2_11 */ // U+055E: "՞" ARMENIAN QUESTION MARK // U+055C: "՜" ARMENIAN EXCLAMATION MARK @@ -2027,9 +2030,9 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EE,\u00EC,\u012F,\u012B", /* morekeys_c */ null, + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s */ null, // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE @@ -2110,13 +2113,13 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_rqm_9qm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL /* keylabel_to_alpha */ "\u05D0\u05D1\u05D2", + /* double_quotes */ "!text/double_rqm_9qm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_rqm_9qm", /* morekeys_s ~ */ null, null, null, null, null, null, @@ -2167,28 +2170,28 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN /* keylabel_to_alpha */ "\u10D0\u10D1\u10D2", + /* double_quotes */ "!text/double_9qm_lqm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", }; /* Locale kk: Kazakh */ private static final String[] TEXTS_kk = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* single_quotes ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2237,14 +2240,14 @@ public final class KeyboardTextsTable { /* Locale km_KH: Khmer (Cambodia) */ private static final String[] TEXTS_km_KH = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+1780: "ក" KHMER LETTER KA // U+1781: "ខ" KHMER LETTER KHA // U+1782: "គ" KHMER LETTER KO /* keylabel_to_alpha */ "\u1780\u1781\u1782", - /* single_quotes ~ */ + /* double_quotes ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2252,7 +2255,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, /* ~ morekeys_cyrillic_a */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL /* morekeys_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", @@ -2261,15 +2264,15 @@ public final class KeyboardTextsTable { /* Locale ky: Kirghiz */ private static final String[] TEXTS_ky = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* single_quotes ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2304,15 +2307,15 @@ public final class KeyboardTextsTable { /* Locale lo_LA: Lao (Laos) */ private static final String[] TEXTS_lo_LA = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0E81: "ກ" LAO LETTER KO // U+0E82: "ຂ" LAO LETTER KHO SUNG // U+0E84: "ຄ" LAO LETTER KHO TAM /* keylabel_to_alpha */ "\u0E81\u0E82\u0E84", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+20AD: "₭" KIP SIGN /* keyspec_currency */ "\u20AD", @@ -2370,12 +2373,12 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u00E7,\u0107", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0146,\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -2464,12 +2467,12 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u00E7,\u0107", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0146,\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -2512,13 +2515,13 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* double_quotes */ "!text/double_9qm_lqm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, @@ -2547,15 +2550,15 @@ public final class KeyboardTextsTable { /* Locale mn_MN: Mongolian (Mongolia) */ private static final String[] TEXTS_mn_MN = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+20AE: "₮" TUGRIK SIGN /* keyspec_currency */ "\u20AE", @@ -2564,15 +2567,15 @@ public final class KeyboardTextsTable { /* Locale mr_IN: Marathi (India) */ private static final String[] TEXTS_mr_IN = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2616,17 +2619,18 @@ public final class KeyboardTextsTable { /* Locale my_MM: Burmese (Myanmar) */ private static final String[] TEXTS_my_MM = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+1000: "က" MYANMAR LETTER KA // U+1001: "ခ" MYANMAR LETTER KHA // U+1002: "ဂ" MYANMAR LETTER GA /* keylabel_to_alpha */ "\u1000\u1001\u1002", - /* single_quotes ~ */ + /* double_quotes ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 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_nordic_row2_11 */ /* morekeys_punctuation */ "!autoColumnOrder!9,\u104A,.,?,!,#,),(,/,;,...,',@,:,-,\",+,\\%,&", // U+104A: "၊" MYANMAR SIGN LITTLE SECTION @@ -2681,11 +2685,11 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", - /* morekeys_i */ null, - /* morekeys_c */ null, + /* morekeys_i ~ */ + null, null, null, + /* ~ keylabel_to_alpha */ /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2709,15 +2713,15 @@ public final class KeyboardTextsTable { /* Locale ne_NP: Nepali (Nepal) */ private static final String[] TEXTS_ne_NP = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN /* keyspec_currency */ "\u0930\u0941.", @@ -2801,11 +2805,11 @@ public final class KeyboardTextsTable { // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B,\u0133", /* morekeys_c */ null, + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_rqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", /* morekeys_s */ null, // U+0133: "ij" LATIN SMALL LIGATURE IJ @@ -2847,11 +2851,11 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u0107,\u00E7,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_rqm", // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u0144,\u00F1", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -2952,9 +2956,9 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B", /* morekeys_c */ null, + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -2973,13 +2977,13 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* double_quotes */ "!text/double_9qm_lqm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, @@ -3001,6 +3005,17 @@ public final class KeyboardTextsTable { /* morekeys_cyrillic_soft_sign */ "\u044A", }; + /* Locale si_LK: Sinhalese (Sri Lanka) */ + private static final String[] TEXTS_si_LK = { + /* morekeys_a ~ */ + null, null, null, null, null, null, + /* ~ morekeys_c */ + // Label for "switch to alphabetic" key. + // U+0D85: "අ" SINHALA LETTER AYANNA + // U+0D86: "ආ" SINHALA LETTER AAYANNA + /* keylabel_to_alpha */ "\u0D85,\u0D86", + }; + /* Locale sk: Slovak */ private static final String[] TEXTS_sk = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -3052,13 +3067,13 @@ public final class KeyboardTextsTable { // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u00E7,\u0107", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+0148: "ň" LATIN SMALL LETTER N WITH CARON // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0148,\u0146,\u00F1,\u0144", - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -3104,9 +3119,9 @@ public final class KeyboardTextsTable { // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u0107", + /* keylabel_to_alpha */ null, /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON /* morekeys_s */ "\u0161", @@ -3127,14 +3142,14 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // END: More keys definitions for Serbian (Cyrillic) // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* double_quotes */ "!text/double_9qm_lqm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, @@ -3217,12 +3232,12 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0148: "ň" LATIN SMALL LETTER N WITH CARON /* morekeys_n */ "\u0144,\u00F1,\u0148", - /* keylabel_to_alpha */ null, /* single_quotes */ null, // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON @@ -3310,10 +3325,10 @@ public final class KeyboardTextsTable { /* morekeys_i */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC", // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u00F1", - /* keylabel_to_alpha */ null, /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S /* morekeys_s */ "\u00DF", @@ -3326,15 +3341,15 @@ public final class KeyboardTextsTable { /* Locale th: Thai */ private static final String[] TEXTS_th = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, - /* ~ morekeys_n */ + null, null, null, null, null, null, + /* ~ morekeys_c */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI /* keylabel_to_alpha */ "\u0E01\u0E02\u0E04", - /* single_quotes ~ */ - null, null, null, null, null, null, null, + /* double_quotes ~ */ + null, null, null, null, null, null, null, null, null, /* ~ morekeys_l */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT /* keyspec_currency */ "\u0E3F", @@ -3388,6 +3403,7 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -3426,7 +3442,7 @@ public final class KeyboardTextsTable { // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u010D", - /* double_quotes ~ */ + /* keylabel_to_alpha ~ */ null, null, null, null, /* ~ single_quotes */ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA @@ -3446,13 +3462,13 @@ public final class KeyboardTextsTable { /* morekeys_a ~ */ null, null, null, null, null, null, /* ~ morekeys_c */ - /* double_quotes */ "!text/double_9qm_lqm", - /* morekeys_n */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* double_quotes */ "!text/double_9qm_lqm", + /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, @@ -3613,10 +3629,10 @@ public final class KeyboardTextsTable { /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC", // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u00F1", - /* keylabel_to_alpha */ null, /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S /* morekeys_s */ "\u00DF", @@ -3686,6 +3702,7 @@ public final class KeyboardTextsTable { // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE // U+010D: "č" LATIN SMALL LETTER C WITH CARON /* morekeys_c */ "\u00E7,\u0107,\u0109,\u010B,\u010D", + /* keylabel_to_alpha */ null, /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -3694,7 +3711,6 @@ public final class KeyboardTextsTable { // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE // U+014B: "ŋ" LATIN SMALL LETTER ENG /* morekeys_n */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B", - /* keylabel_to_alpha */ null, /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -3767,21 +3783,21 @@ public final class KeyboardTextsTable { "ar" , TEXTS_ar, /* 55/110 Arabic */ "az_AZ" , TEXTS_az_AZ, /* 8/ 18 Azerbaijani (Azerbaijan) */ "be_BY" , TEXTS_be_BY, /* 9/ 32 Belarusian (Belarus) */ - "bg" , TEXTS_bg, /* 2/ 9 Bulgarian */ + "bg" , TEXTS_bg, /* 2/ 8 Bulgarian */ "ca" , TEXTS_ca, /* 11/ 95 Catalan */ "cs" , TEXTS_cs, /* 17/ 21 Czech */ "da" , TEXTS_da, /* 19/ 54 Danish */ "de" , TEXTS_de, /* 16/ 62 German */ - "el" , TEXTS_el, /* 1/ 9 Greek */ + "el" , TEXTS_el, /* 1/ 7 Greek */ "en" , TEXTS_en, /* 8/ 11 English */ "eo" , TEXTS_eo, /* 26/118 Esperanto */ "es" , TEXTS_es, /* 8/ 55 Spanish */ "et_EE" , TEXTS_et_EE, /* 22/ 27 Estonian (Estonia) */ - "eu_ES" , TEXTS_eu_ES, /* 7/ 8 Basque (Spain) */ + "eu_ES" , TEXTS_eu_ES, /* 7/ 9 Basque (Spain) */ "fa" , TEXTS_fa, /* 58/125 Persian */ "fi" , TEXTS_fi, /* 10/ 54 Finnish */ "fr" , TEXTS_fr, /* 13/ 62 French */ - "gl_ES" , TEXTS_gl_ES, /* 7/ 8 Gallegan (Spain) */ + "gl_ES" , TEXTS_gl_ES, /* 7/ 9 Gallegan (Spain) */ "hi" , TEXTS_hi, /* 23/ 53 Hindi */ "hr" , TEXTS_hr, /* 9/ 20 Croatian */ "hu" , TEXTS_hu, /* 9/ 20 Hungarian */ @@ -3808,13 +3824,14 @@ public final class KeyboardTextsTable { "rm" , TEXTS_rm, /* 1/ 2 Raeto-Romance */ "ro" , TEXTS_ro, /* 6/ 15 Romanian */ "ru" , TEXTS_ru, /* 9/ 32 Russian */ + "si_LK" , TEXTS_si_LK, /* 1/ 7 Sinhalese (Sri Lanka) */ "sk" , TEXTS_sk, /* 20/ 22 Slovak */ "sl" , TEXTS_sl, /* 8/ 20 Slovenian */ "sr" , TEXTS_sr, /* 11/ 93 Serbian */ "sv" , TEXTS_sv, /* 21/ 54 Swedish */ "sw" , TEXTS_sw, /* 9/ 18 Swahili */ "th" , TEXTS_th, /* 2/ 17 Thai */ - "tl" , TEXTS_tl, /* 7/ 8 Tagalog */ + "tl" , TEXTS_tl, /* 7/ 9 Tagalog */ "tr" , TEXTS_tr, /* 7/ 18 Turkish */ "uk" , TEXTS_uk, /* 11/ 87 Ukrainian */ "vi" , TEXTS_vi, /* 8/ 17 Vietnamese */ diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 335e52fef..1b5791809 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -174,8 +174,8 @@ public final class BinaryDictionary extends Dictionary { private static native int getFormatVersionNative(long dict); private static native int getProbabilityNative(long dict, int[] word); private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word); - private static native int getBigramProbabilityNative(long dict, int[] word0, - boolean isBeginningOfSentence, int[] word1); + private static native int getNgramProbabilityNative(long dict, int[][] prevWordCodePointArrays, + boolean[] isBeginningOfSentenceArray, int[] word); private static native void getWordPropertyNative(long dict, int[] word, boolean isBeginningOfSentence, int[] outCodePoints, boolean[] outFlags, int[] outProbabilityInfo, ArrayList<int[]> outBigramTargets, @@ -186,18 +186,19 @@ public final class BinaryDictionary extends Dictionary { private static native void getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodePoints, int inputSize, int[] suggestOptions, - int[] prevWordCodePointArray, boolean isBeginningOfSentence, + int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence, float[] inOutLanguageWeight); - private static native boolean addUnigramWordNative(long dict, int[] word, int probability, + private static native boolean addUnigramEntryNative(long dict, int[] word, int probability, int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence, boolean isNotAWord, boolean isBlacklisted, int timestamp); - private static native boolean removeUnigramWordNative(long dict, int[] word); - private static native boolean addBigramWordsNative(long dict, int[] word0, - boolean isBeginningOfSentence, int[] word1, int probability, int timestamp); - private static native boolean removeBigramWordsNative(long dict, int[] word0, - boolean isBeginningOfSentence, int[] word1); + private static native boolean removeUnigramEntryNative(long dict, int[] word); + private static native boolean addNgramEntryNative(long dict, + int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, + int[] word, int probability, int timestamp); + private static native boolean removeNgramEntryNative(long dict, + int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word); private static native int addMultipleDictionaryEntriesNative(long dict, LanguageModelParam[] languageModelParams, int startIndex); private static native String getPropertyNative(long dict, String query); @@ -262,9 +263,8 @@ public final class BinaryDictionary extends Dictionary { } final DicTraverseSession session = getTraverseSession(sessionId); Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE); - // TODO: toLowerCase in the native code - final int[] prevWordCodePointArray = (null == prevWordsInfo.mPrevWord) - ? null : StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); + prevWordsInfo.outputToArray(session.mPrevWordCodePointArrays, + session.mIsBeginningOfSentenceArray); final InputPointers inputPointers = composer.getInputPointers(); final boolean isGesture = composer.isBatchMode(); final int inputSize; @@ -286,13 +286,13 @@ public final class BinaryDictionary extends Dictionary { } else { session.mInputOutputLanguageWeight[0] = Dictionary.NOT_A_LANGUAGE_WEIGHT; } - // proximityInfo and/or prevWordForBigrams may not be null. + // TOOD: Pass multiple previous words information for n-gram. getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), inputPointers.getYCoordinates(), inputPointers.getTimes(), inputPointers.getPointerIds(), session.mInputCodePoints, inputSize, - session.mNativeSuggestOptions.getOptions(), prevWordCodePointArray, - prevWordsInfo.mIsBeginningOfSentence, session.mOutputSuggestionCount, + session.mNativeSuggestOptions.getOptions(), session.mPrevWordCodePointArrays, + session.mIsBeginningOfSentenceArray, session.mOutputSuggestionCount, session.mOutputCodePoints, session.mOutputScores, session.mSpaceIndices, session.mOutputTypes, session.mOutputAutoCommitFirstWordConfidence, session.mInputOutputLanguageWeight); @@ -355,10 +355,13 @@ public final class BinaryDictionary extends Dictionary { if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { return NOT_A_PROBABILITY; } - final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); - final int[] codePoints1 = StringUtils.toCodePointArray(word); - return getBigramProbabilityNative(mNativeDict, codePoints0, - prevWordsInfo.mIsBeginningOfSentence, codePoints1); + final int[][] prevWordCodePointArrays = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][]; + final boolean[] isBeginningOfSentenceArray = + new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray); + final int[] wordCodePoints = StringUtils.toCodePointArray(word); + return getNgramProbabilityNative(mNativeDict, prevWordCodePointArrays, + isBeginningOfSentenceArray, wordCodePoints); } public WordProperty getWordProperty(final String word, final boolean isBeginningOfSentence) { @@ -422,7 +425,7 @@ public final class BinaryDictionary extends Dictionary { final int[] codePoints = StringUtils.toCodePointArray(word); final int[] shortcutTargetCodePoints = (shortcutTarget != null) ? StringUtils.toCodePointArray(shortcutTarget) : null; - if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, + if (!addUnigramEntryNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) { return false; } @@ -436,7 +439,7 @@ public final class BinaryDictionary extends Dictionary { return false; } final int[] codePoints = StringUtils.toCodePointArray(word); - if (!removeUnigramWordNative(mNativeDict, codePoints)) { + if (!removeUnigramEntryNative(mNativeDict, codePoints)) { return false; } mHasUpdated = true; @@ -449,10 +452,13 @@ public final class BinaryDictionary extends Dictionary { if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { return false; } - final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); - final int[] codePoints1 = StringUtils.toCodePointArray(word); - if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence, - codePoints1, probability, timestamp)) { + final int[][] prevWordCodePointArrays = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][]; + final boolean[] isBeginningOfSentenceArray = + new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray); + final int[] wordCodePoints = StringUtils.toCodePointArray(word); + if (!addNgramEntryNative(mNativeDict, prevWordCodePointArrays, + isBeginningOfSentenceArray, wordCodePoints, probability, timestamp)) { return false; } mHasUpdated = true; @@ -464,10 +470,13 @@ public final class BinaryDictionary extends Dictionary { if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { return false; } - final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); - final int[] codePoints1 = StringUtils.toCodePointArray(word); - if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence, - codePoints1)) { + final int[][] prevWordCodePointArrays = new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][]; + final boolean[] isBeginningOfSentenceArray = + new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray); + final int[] wordCodePoints = StringUtils.toCodePointArray(word); + if (!removeNgramEntryNative(mNativeDict, prevWordCodePointArrays, + isBeginningOfSentenceArray, wordCodePoints)) { return false; } mHasUpdated = true; diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index f9339361a..43af66eb7 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -166,6 +166,10 @@ public final class Constants { // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h public static final int DICTIONARY_MAX_WORD_LENGTH = 48; + // (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported in Java side. Needs to modify + // MAX_PREV_WORD_COUNT_FOR_N_GRAM in native/jni/src/defines.h for suggestions. + public static final int MAX_PREV_WORD_COUNT_FOR_N_GRAM = 2; + // Key events coming any faster than this are long-presses. public static final int LONG_PRESS_MILLISECONDS = 200; // TODO: Set this value appropriately. @@ -216,6 +220,7 @@ public final class Constants { public static final String REGEXP_PERIOD = "\\."; public static final String STRING_SPACE = " "; + public static final String STRING_PERIOD_AND_SPACE = ". "; /** * Special keys code. Must be negative. diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 96160fa4e..ad14c06ef 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -233,19 +233,19 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { final int wordLen = StringUtils.codePointCount(word); if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { if (DEBUG) { - Log.d(TAG, "addName " + name + ", " + word + ", " - + prevWordsInfo.mPrevWord); + Log.d(TAG, "addName " + name + ", " + word + ", " + prevWordsInfo); } runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); - if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && mUseFirstLastBigrams) { + if (!prevWordsInfo.isValid() && mUseFirstLastBigrams) { runGCIfRequiredLocked(true /* mindsBlockByGC */); addNgramEntryLocked(prevWordsInfo, word, FREQUENCY_FOR_CONTACTS_BIGRAM, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } - prevWordsInfo = new PrevWordsInfo(word); + prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo( + new PrevWordsInfo.WordInfo(word)); } } } diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 8bbf426e5..b341f623e 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -28,6 +28,10 @@ public final class DicTraverseSession { // Must be equal to MAX_RESULTS in native/jni/src/defines.h private static final int MAX_RESULTS = 18; public final int[] mInputCodePoints = new int[Constants.DICTIONARY_MAX_WORD_LENGTH]; + public final int[][] mPrevWordCodePointArrays = + new int[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM][]; + public final boolean[] mIsBeginningOfSentenceArray = + new boolean[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; public final int[] mOutputSuggestionCount = new int[1]; public final int[] mOutputCodePoints = new int[Constants.DICTIONARY_MAX_WORD_LENGTH * MAX_RESULTS]; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 304c450ab..b8feb2278 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -23,6 +23,7 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.PrevWordsInfo.WordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.personalization.ContextualDictionary; import com.android.inputmethod.latin.personalization.PersonalizationDataChunk; @@ -407,13 +408,14 @@ public class DictionaryFacilitator { final boolean blockPotentiallyOffensive) { final Dictionaries dictionaries = mDictionaries; final String[] words = suggestion.split(Constants.WORD_SEPARATOR); + PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo; for (int i = 0; i < words.length; i++) { final String currentWord = words[i]; - final PrevWordsInfo prevWordsInfoForCurrentWord = - (i == 0) ? prevWordsInfo : new PrevWordsInfo(words[i - 1]); final boolean wasCurrentWordAutoCapitalized = (i == 0) ? wasAutoCapitalized : false; addWordToUserHistory(dictionaries, prevWordsInfoForCurrentWord, currentWord, wasCurrentWordAutoCapitalized, timeStampInSeconds, blockPotentiallyOffensive); + prevWordsInfoForCurrentWord = + prevWordsInfoForCurrentWord.getNextPrevWordsInfo(new WordInfo(currentWord)); } } @@ -639,7 +641,8 @@ public class DictionaryFacilitator { contextualDict.addNgramEntry(prevWordsInfo, phrase[i], bigramProbabilityForWords, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } - prevWordsInfo = new PrevWordsInfo(phrase[i]); + prevWordsInfo = + prevWordsInfo.getNextPrevWordsInfo(new PrevWordsInfo.WordInfo(phrase[i])); } } diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java index 42b311c69..5dda44445 100644 --- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java +++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java @@ -16,47 +16,122 @@ package com.android.inputmethod.latin; +import java.util.Arrays; + +import com.android.inputmethod.latin.utils.StringUtils; + /** * Class to represent information of previous words. This class is used to add n-gram entries * into binary dictionaries, to get predictions, and to get suggestions. */ -// TODO: Support multiple previous words for n-gram. public class PrevWordsInfo { - public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO = new PrevWordsInfo(null); + public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO = + new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO); public static final PrevWordsInfo BEGINNING_OF_SENTENCE = new PrevWordsInfo(); - // The word immediately before the considered word. null means we don't have any context - // including the "beginning of sentence context" - we just don't know what to predict. - // An example of that is after a comma. - // For simplicity of implementation, this may also be null transiently after the WordComposer - // was reset and before starting a new composing word, but we should never be calling - // getSuggetions* in this situation. - // This is an empty string when mIsBeginningOfSentence is true. - public final String mPrevWord; + /** + * Word information used to represent previous words information. + */ + public static class WordInfo { + public static final WordInfo EMPTY_WORD_INFO = new WordInfo(null); + public static final WordInfo BEGINNING_OF_SENTENCE = new WordInfo(); + + // This is an empty string when mIsBeginningOfSentence is true. + public final String mWord; + // TODO: Have sentence separator. + // Whether the current context is beginning of sentence or not. This is true when composing + // at the beginning of an input field or composing a word after a sentence separator. + public final boolean mIsBeginningOfSentence; + + // Beginning of sentence. + public WordInfo() { + mWord = ""; + mIsBeginningOfSentence = true; + } + + public WordInfo(final String word) { + mWord = word; + mIsBeginningOfSentence = false; + } + + public boolean isValid() { + return mWord != null; + } + } - // TODO: Have sentence separator. - // Whether the current context is beginning of sentence or not. This is true when composing at - // the beginning of an input field or composing a word after a sentence separator. - public final boolean mIsBeginningOfSentence; + // The words immediately before the considered word. EMPTY_WORD_INFO element means we don't + // have any context for that previous word including the "beginning of sentence context" - we + // just don't know what to predict using the information. An example of that is after a comma. + // For simplicity of implementation, elements may also be EMPTY_WORD_INFO transiently after the + // WordComposer was reset and before starting a new composing word, but we should never be + // calling getSuggetions* in this situation. + public WordInfo[] mPrevWordsInfo = new WordInfo[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; // Beginning of sentence. public PrevWordsInfo() { - mPrevWord = ""; - mIsBeginningOfSentence = true; + mPrevWordsInfo[0] = WordInfo.BEGINNING_OF_SENTENCE; + Arrays.fill(mPrevWordsInfo, 1 /* start */, mPrevWordsInfo.length, WordInfo.EMPTY_WORD_INFO); } - public PrevWordsInfo(final String prevWord) { - mPrevWord = prevWord; - mIsBeginningOfSentence = false; + // Construct from the previous word information. + public PrevWordsInfo(final WordInfo prevWordInfo) { + mPrevWordsInfo[0] = prevWordInfo; + Arrays.fill(mPrevWordsInfo, 1 /* start */, mPrevWordsInfo.length, WordInfo.EMPTY_WORD_INFO); + } + + // Construct from WordInfo array. n-th element represents (n+1)-th previous word's information. + public PrevWordsInfo(final WordInfo[] prevWordsInfo) { + for (int i = 0; i < Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM; i++) { + mPrevWordsInfo[i] = + (prevWordsInfo.length > i) ? prevWordsInfo[i] : WordInfo.EMPTY_WORD_INFO; + } + } + + // Create next prevWordsInfo using current prevWordsInfo. + public PrevWordsInfo getNextPrevWordsInfo(final WordInfo wordInfo) { + final WordInfo[] prevWordsInfo = new WordInfo[Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo[0] = wordInfo; + for (int i = 1; i < prevWordsInfo.length; i++) { + prevWordsInfo[i] = mPrevWordsInfo[i - 1]; + } + return new PrevWordsInfo(prevWordsInfo); } public boolean isValid() { - return mPrevWord != null; + return mPrevWordsInfo[0].isValid(); + } + + public void outputToArray(final int[][] codePointArrays, + final boolean[] isBeginningOfSentenceArray) { + for (int i = 0; i < mPrevWordsInfo.length; i++) { + final WordInfo wordInfo = mPrevWordsInfo[i]; + if (wordInfo == null || !wordInfo.isValid()) { + codePointArrays[i] = new int[0]; + isBeginningOfSentenceArray[i] = false; + continue; + } + codePointArrays[i] = StringUtils.toCodePointArray(wordInfo.mWord); + isBeginningOfSentenceArray[i] = wordInfo.mIsBeginningOfSentence; + } } @Override public String toString() { - return "PrevWord: " + mPrevWord + ", isBeginningOfSentence: " - + mIsBeginningOfSentence + "."; + final StringBuffer builder = new StringBuffer(); + for (int i = 0; i < mPrevWordsInfo.length; i++) { + final WordInfo wordInfo = mPrevWordsInfo[i]; + builder.append("PrevWord["); + builder.append(i); + builder.append("]: "); + if (!wordInfo.isValid()) { + builder.append("Empty. "); + continue; + } + builder.append(wordInfo.mWord); + builder.append(", isBeginningOfSentence: "); + builder.append(wordInfo.mIsBeginningOfSentence); + builder.append(". "); + } + return builder.toString(); } } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 96476b2ee..c027892ba 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -603,7 +603,7 @@ public final class RichInputConnection { || spacingAndPunctuations.isWordConnector(lastChar)) { return PrevWordsInfo.EMPTY_PREV_WORDS_INFO; } - return new PrevWordsInfo(nthPrevWord); + return new PrevWordsInfo(new PrevWordsInfo.WordInfo(nthPrevWord)); } /** @@ -741,13 +741,12 @@ public final class RichInputConnection { // Here we test whether we indeed have a period and a space before us. This should not // be needed, but it's there just in case something went wrong. final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0); - final String periodSpace = ". "; - if (!TextUtils.equals(periodSpace, textBeforeCursor)) { + if (!TextUtils.equals(Constants.STRING_PERIOD_AND_SPACE, textBeforeCursor)) { // Theoretically we should not be coming here if there isn't ". " before the // cursor, but the application may be changing the text while we are typing, so // anything goes. We should not crash. Log.d(TAG, "Tried to revert double-space combo but we didn't find " - + "\"" + periodSpace + "\" just before the cursor."); + + "\"" + Constants.STRING_PERIOD_AND_SPACE + "\" just before the cursor."); return false; } // Double-space results in ". ". A backspace to cancel this should result in a single diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 9d03e8a43..e43db352d 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -123,39 +123,27 @@ public final class Suggest { suggestionsContainer.set(i, transformedWordInfo); } } - SuggestedWordInfo.removeDups(typedWord, suggestionsContainer); + final boolean didRemoveTypedWord = + SuggestedWordInfo.removeDups(typedWord, suggestionsContainer); - // If resumed, then we don't want to upcase everything: resuming on a fully-capitalized - // words is rarely done to switch to another fully-capitalized word, but usually to a - // normal, non-capitalized suggestion. - final String firstSuggestion; + final SuggestedWordInfo firstSuggestedWordInfo; final String whitelistedWord; - if (suggestionResults.isEmpty()) { - whitelistedWord = firstSuggestion = null; + if (suggestionsContainer.isEmpty()) { + firstSuggestedWordInfo = null; + whitelistedWord = null; } else { - final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo( - suggestionResults.first(), suggestionResults.mLocale, - shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized, - trailingSingleQuotesCount); - firstSuggestion = firstSuggestedWordInfo.mWord; + firstSuggestedWordInfo = suggestionsContainer.get(0); if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { whitelistedWord = null; } else { - whitelistedWord = firstSuggestion; + whitelistedWord = firstSuggestedWordInfo.mWord; } } - // We allow auto-correction if we have a whitelisted word, or if the word is not a valid - // word of more than 1 char, except if the first suggestion is the same as the typed string - // because in this case if it's strong enough to auto-correct that will mistakenly designate - // the second candidate for auto-correction. - // TODO: stop relying on indices to find where is the auto-correction in the suggested - // words, and correct this test. - final boolean allowsToBeAutoCorrected = (null != whitelistedWord - && !whitelistedWord.equals(typedWord)) - || (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord( - consideredWord, isOnlyFirstCharCapitalized) - && !typedWord.equals(firstSuggestion)); + // We allow auto-correction if we have a whitelisted word, or if the word had more than + // one char and was not suggested. + final boolean allowsToBeAutoCorrected = (null != whitelistedWord) + || (consideredWord.length() > 1 && !didRemoveTypedWord); final boolean hasAutoCorrection; // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because @@ -164,10 +152,10 @@ public final class Suggest { // the current settings. It may also be useful to know, when the setting is off, whether // the word *would* have been auto-corrected. if (!isCorrectionEnabled || !allowsToBeAutoCorrected || isPrediction - || suggestionResults.isEmpty() || wordComposer.hasDigits() + || null == firstSuggestedWordInfo || wordComposer.hasDigits() || wordComposer.isMostlyCaps() || wordComposer.isResumed() || !mDictionaryFacilitator.hasInitializedMainDictionary() - || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { + || firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { // If we don't have a main dictionary, we never want to auto-correct. The reason for // this is, the user may have a contact whose name happens to match a valid word in // their language, and it will unexpectedly auto-correct. For example, if the user @@ -179,7 +167,7 @@ public final class Suggest { hasAutoCorrection = false; } else { hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( - suggestionResults.first(), consideredWord, mAutoCorrectionThreshold); + firstSuggestedWordInfo, consideredWord, mAutoCorrectionThreshold); } if (!TextUtils.isEmpty(typedWord)) { diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index f22af7991..e587b18c9 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -330,29 +330,37 @@ public class SuggestedWords { } // This will always remove the higher index if a duplicate is found. - public static void removeDups(final String typedWord, + public static boolean removeDups(final String typedWord, ArrayList<SuggestedWordInfo> candidates) { if (candidates.isEmpty()) { - return; + return false; } + final boolean didRemoveTypedWord; if (!TextUtils.isEmpty(typedWord)) { - removeSuggestedWordInfoFrom(typedWord, candidates, -1 /* startIndexExclusive */); + didRemoveTypedWord = removeSuggestedWordInfoFrom(typedWord, candidates, + -1 /* startIndexExclusive */); + } else { + didRemoveTypedWord = false; } for (int i = 0; i < candidates.size(); ++i) { removeSuggestedWordInfoFrom(candidates.get(i).mWord, candidates, i /* startIndexExclusive */); } + return didRemoveTypedWord; } - private static void removeSuggestedWordInfoFrom(final String word, + private static boolean removeSuggestedWordInfoFrom(final String word, final ArrayList<SuggestedWordInfo> candidates, final int startIndexExclusive) { + boolean didRemove = false; for (int i = startIndexExclusive + 1; i < candidates.size(); ++i) { final SuggestedWordInfo previous = candidates.get(i); if (word.equals(previous.mWord)) { + didRemove = true; candidates.remove(i); --i; } } + return didRemove; } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index faa558257..ec57cd71f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -399,7 +399,7 @@ public final class InputLogic { } boolean didAutoCorrect = false; - if (Event.NOT_A_KEY_CODE != event.mKeyCode) { + if (event.isFunctionalKeyEvent()) { // A special key, like delete, shift, emoji, or the settings key. switch (event.mKeyCode) { case Constants.CODE_DELETE: @@ -728,13 +728,13 @@ public final class InputLogic { mConnection.setComposingText(getTextWithUnderline( mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = maybeStripSpace(inputTransaction, - inputTransaction.mEvent.isSuggestionStripPress()); + final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead( + inputTransaction, inputTransaction.mEvent.isSuggestionStripPress()); - sendKeyCodePoint(settingsValues, codePoint); - - if (swapWeakSpace && swapSwapperAndSpace(inputTransaction)) { + if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) { mSpaceState = SpaceState.WEAK; + } else { + sendKeyCodePoint(settingsValues, codePoint); } // In case the "add to dictionary" hint was still displayed. mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); @@ -779,7 +779,8 @@ public final class InputLogic { } } - final boolean swapWeakSpace = maybeStripSpace(inputTransaction, isFromSuggestionStrip); + final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead( + inputTransaction, isFromSuggestionStrip); final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint && mConnection.isInsideDoubleQuoteOrAfterDigit(); @@ -803,14 +804,14 @@ public final class InputLogic { promotePhantomSpace(settingsValues); } - if (!shouldAvoidSendingCode) { - sendKeyCodePoint(settingsValues, codePoint); - } - - if (Constants.CODE_SPACE == codePoint) { - if (maybeDoubleSpacePeriod(inputTransaction)) { - mSpaceState = SpaceState.DOUBLE; - } else if (!mSuggestedWords.isPunctuationSuggestions()) { + if (tryPerformDoubleSpacePeriod(inputTransaction)) { + mSpaceState = SpaceState.DOUBLE; + inputTransaction.setRequiresUpdateSuggestions(); + } else if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) { + mSpaceState = SpaceState.SWAP_PUNCTUATION; + mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); + } else if (Constants.CODE_SPACE == codePoint) { + if (!mSuggestedWords.isPunctuationSuggestions()) { mSpaceState = SpaceState.WEAK; } @@ -818,12 +819,12 @@ public final class InputLogic { if (wasComposingWord || mSuggestedWords.isEmpty()) { inputTransaction.setRequiresUpdateSuggestions(); } + + if (!shouldAvoidSendingCode) { + sendKeyCodePoint(settingsValues, codePoint); + } } else { - if (swapWeakSpace) { - if (swapSwapperAndSpace(inputTransaction)) { - mSpaceState = SpaceState.SWAP_PUNCTUATION; - } - } else if ((SpaceState.PHANTOM == inputTransaction.mSpaceState + if ((SpaceState.PHANTOM == inputTransaction.mSpaceState && settingsValues.isUsuallyFollowedBySpace(codePoint)) || (Constants.CODE_DOUBLE_QUOTE == codePoint && isInsideDoubleQuoteOrAfterDigit)) { @@ -841,6 +842,8 @@ public final class InputLogic { mSpaceState = SpaceState.PHANTOM; } + sendKeyCodePoint(settingsValues, codePoint); + // Set punctuation right away. onUpdateSelection will fire but tests whether it is // already displayed or not, so it's okay. mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); @@ -1008,17 +1011,16 @@ public final class InputLogic { * @param inputTransaction The transaction in progress. * @return true if the swap has been performed, false if it was prevented by preliminary checks. */ - private boolean swapSwapperAndSpace(final InputTransaction inputTransaction) { - final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); - // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. - if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) { - mConnection.deleteSurroundingText(2, 0); - final String text = lastTwo.charAt(1) + " "; - mConnection.commitText(text, 1); - inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); - return true; + private boolean trySwapSwapperAndSpace(final InputTransaction inputTransaction) { + final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); + if (Constants.CODE_SPACE != codePointBeforeCursor) { + return false; } - return false; + mConnection.deleteSurroundingText(1, 0); + final String text = inputTransaction.mEvent.getTextToCommit() + " "; + mConnection.commitText(text, 1); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + return true; } /* @@ -1027,8 +1029,8 @@ public final class InputLogic { * @param isFromSuggestionStrip Whether this code point is coming from the suggestion strip. * @return whether we should swap the space instead of removing it. */ - private boolean maybeStripSpace(final InputTransaction inputTransaction, - final boolean isFromSuggestionStrip) { + private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead( + final InputTransaction inputTransaction, final boolean isFromSuggestionStrip) { final int codePoint = inputTransaction.mEvent.mCodePoint; if (Constants.CODE_ENTER == codePoint && SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) { @@ -1069,36 +1071,37 @@ public final class InputLogic { * period-space sequence of characters. This typically happens when the user presses space * twice in a row quickly. * This method will check that the double-space-to-period is active in settings, that the - * two spaces have been input close enough together, and that the previous character allows - * for the transformation to take place. If all of these conditions are fulfilled, this - * method applies the transformation and returns true. Otherwise, it does nothing and - * returns false. + * two spaces have been input close enough together, that the typed character is a space + * and that the previous character allows for the transformation to take place. If all of + * these conditions are fulfilled, this method applies the transformation and returns true. + * Otherwise, it does nothing and returns false. * * @param inputTransaction The transaction in progress. * @return true if we applied the double-space-to-period transformation, false otherwise. */ - private boolean maybeDoubleSpacePeriod(final InputTransaction inputTransaction) { - if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod) return false; - // This can't happen right now because we don't call this method when the code is not space - if (Constants.CODE_SPACE != inputTransaction.mEvent.mCodePoint) return false; - if (!isDoubleSpacePeriodCountdownActive(inputTransaction)) return false; - // We only do this when we see two spaces and an accepted code point before the cursor. - // The code point may be a surrogate pair but the two spaces may not, so we need 4 chars. - final CharSequence lastThree = mConnection.getTextBeforeCursor(4, 0); - if (null == lastThree) return false; - final int length = lastThree.length(); - if (length < 3) return false; - if (lastThree.charAt(length - 1) != Constants.CODE_SPACE) return false; - if (lastThree.charAt(length - 2) != Constants.CODE_SPACE) return false; - // We know there are spaces in pos -1 and -2, and we have at least three chars. - // If we have only three chars, isSurrogatePairs can't return true as charAt(1) is a space, - // so this is fine. + private boolean tryPerformDoubleSpacePeriod(final InputTransaction inputTransaction) { + // Check the setting, the typed character and the countdown. If any of the conditions is + // not fulfilled, return false. + if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod + || Constants.CODE_SPACE != inputTransaction.mEvent.mCodePoint + || !isDoubleSpacePeriodCountdownActive(inputTransaction)) { + return false; + } + // We only do this when we see one space and an accepted code point before the cursor. + // The code point may be a surrogate pair but the space may not, so we need 3 chars. + final CharSequence lastTwo = mConnection.getTextBeforeCursor(3, 0); + if (null == lastTwo) return false; + final int length = lastTwo.length(); + if (length < 2) return false; + if (lastTwo.charAt(length - 1) != Constants.CODE_SPACE) return false; + // We know there is a space in pos -1, and we have at least two chars. If we have only two + // chars, isSurrogatePairs can't return true as charAt(1) is a space, so this is fine. final int firstCodePoint = - Character.isSurrogatePair(lastThree.charAt(0), lastThree.charAt(1)) ? - Character.codePointAt(lastThree, 0) : lastThree.charAt(length - 3); + Character.isSurrogatePair(lastTwo.charAt(0), lastTwo.charAt(1)) ? + Character.codePointAt(lastTwo, length - 3) : lastTwo.charAt(length - 2); if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) { cancelDoubleSpacePeriodCountdown(); - mConnection.deleteSurroundingText(2, 0); + mConnection.deleteSurroundingText(1, 0); final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations .mSentenceSeparatorAndSpace; mConnection.commitText(textToInsert, 1); @@ -1533,7 +1536,8 @@ public final class InputLogic { } else { return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? PrevWordsInfo.BEGINNING_OF_SENTENCE : - new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString()); + new PrevWordsInfo(new PrevWordsInfo.WordInfo( + mLastComposedWord.mCommittedWord.toString())); } } diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index 3916fc24c..a98b0f156 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -60,7 +60,7 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary, final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid, final int timestamp, final DistracterFilter distracterFilter) { - final String prevWord = prevWordsInfo.mPrevWord; + final String prevWord = prevWordsInfo.mPrevWordsInfo[0].mWord; if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH || (prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) { return; @@ -75,7 +75,13 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas return; } if (null != prevWord) { - userHistoryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp); + if (prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence) { + // Beginning-of-Sentence n-gram entry is treated as a n-gram entry of invalid word. + userHistoryDictionary.addNgramEntry(prevWordsInfo, word, + FREQUENCY_FOR_WORDS_NOT_IN_DICTS, timestamp); + } else { + userHistoryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp); + } } } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 8d495646d..d0316242b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -37,6 +37,7 @@ import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.StringUtils; import java.lang.ref.WeakReference; @@ -78,40 +79,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList = new HashSet<>(); - public static final int SCRIPT_LATIN = 0; - public static final int SCRIPT_CYRILLIC = 1; - public static final int SCRIPT_GREEK = 2; public static final String SINGLE_QUOTE = "\u0027"; public static final String APOSTROPHE = "\u2019"; - private static final TreeMap<String, Integer> mLanguageToScript; - static { - // List of the supported languages and their associated script. We won't check - // words written in another script than the selected script, because we know we - // don't have those in our dictionary so we will underline everything and we - // will never have any suggestions, so it makes no sense checking them, and this - // is done in {@link #shouldFilterOut}. Also, the script is used to choose which - // proximity to pass to the dictionary descent algorithm. - // IMPORTANT: this only contains languages - do not write countries in there. - // Only the language is searched from the map. - mLanguageToScript = new TreeMap<>(); - mLanguageToScript.put("cs", SCRIPT_LATIN); - mLanguageToScript.put("da", SCRIPT_LATIN); - mLanguageToScript.put("de", SCRIPT_LATIN); - mLanguageToScript.put("el", SCRIPT_GREEK); - mLanguageToScript.put("en", SCRIPT_LATIN); - mLanguageToScript.put("es", SCRIPT_LATIN); - mLanguageToScript.put("fi", SCRIPT_LATIN); - mLanguageToScript.put("fr", SCRIPT_LATIN); - mLanguageToScript.put("hr", SCRIPT_LATIN); - mLanguageToScript.put("it", SCRIPT_LATIN); - mLanguageToScript.put("lt", SCRIPT_LATIN); - mLanguageToScript.put("lv", SCRIPT_LATIN); - mLanguageToScript.put("nb", SCRIPT_LATIN); - mLanguageToScript.put("nl", SCRIPT_LATIN); - mLanguageToScript.put("pt", SCRIPT_LATIN); - mLanguageToScript.put("sl", SCRIPT_LATIN); - mLanguageToScript.put("ru", SCRIPT_CYRILLIC); - } @Override public void onCreate() { super.onCreate(); @@ -122,22 +91,13 @@ public final class AndroidSpellCheckerService extends SpellCheckerService onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY); } - public static int getScriptFromLocale(final Locale locale) { - final Integer script = mLanguageToScript.get(locale.getLanguage()); - if (null == script) { - throw new RuntimeException("We have been called with an unsupported language: \"" - + locale.getLanguage() + "\". Framework bug?"); - } - return script; - } - private static String getKeyboardLayoutNameForScript(final int script) { switch (script) { - case AndroidSpellCheckerService.SCRIPT_LATIN: + case ScriptUtils.SCRIPT_LATIN: return "qwerty"; - case AndroidSpellCheckerService.SCRIPT_CYRILLIC: + case ScriptUtils.SCRIPT_CYRILLIC: return "east_slavic"; - case AndroidSpellCheckerService.SCRIPT_GREEK: + case ScriptUtils.SCRIPT_GREEK: return "greek"; default: throw new RuntimeException("Wrong script supplied: " + script); @@ -413,7 +373,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService } public DictAndKeyboard createDictAndKeyboard(final Locale locale) { - final int script = getScriptFromLocale(locale); + final int script = ScriptUtils.getScriptFromLocale(locale); final String keyboardLayoutName = getKeyboardLayoutNameForScript(script); final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype( locale.toString(), keyboardLayoutName, null); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index 55274cfe2..6bfd354ea 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -61,7 +61,8 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck final int offset = ssi.getOffsetAt(i); final int length = ssi.getLengthAt(i); final String subText = typedText.substring(offset, offset + length); - final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(currentWord); + final PrevWordsInfo prevWordsInfo = + new PrevWordsInfo(new PrevWordsInfo.WordInfo(currentWord)); currentWord = subText; if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { continue; @@ -203,7 +204,8 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck } else { prevWord = null; } - final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(prevWord); + final PrevWordsInfo prevWordsInfo = + new PrevWordsInfo(new PrevWordsInfo.WordInfo(prevWord)); retval[i] = onGetSuggestionsInternal(textInfos[i], prevWordsInfo, suggestionsLimit); retval[i].setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 54eebe399..be33f339d 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -36,6 +36,7 @@ import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; @@ -72,10 +73,10 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { // TODO: Support n-gram input private static String generateKey(final String query, final PrevWordsInfo prevWordsInfo) { - if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWordsInfo.mPrevWord)) { + if (TextUtils.isEmpty(query) || !prevWordsInfo.isValid()) { return query; } - return query + CHAR_DELIMITER + prevWordsInfo.mPrevWord; + return query + CHAR_DELIMITER + prevWordsInfo; } public SuggestionsParams getSuggestionsFromCache(String query, @@ -116,7 +117,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { final String localeString = getLocale(); mDictionaryPool = mService.getDictionaryPool(localeString); mLocale = LocaleUtils.constructLocaleFromString(localeString); - mScript = AndroidSpellCheckerService.getScriptFromLocale(mLocale); + mScript = ScriptUtils.getScriptFromLocale(mLocale); } @Override @@ -125,44 +126,6 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { cres.unregisterContentObserver(mObserver); } - /* - * Returns whether the code point is a letter that makes sense for the specified - * locale for this spell checker. - * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml - * and is limited to EFIGS languages and Russian. - * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters - * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters. - */ - private static boolean isLetterCheckableByLanguage(final int codePoint, - final int script) { - switch (script) { - case AndroidSpellCheckerService.SCRIPT_LATIN: - // Our supported latin script dictionaries (EFIGS) at the moment only include - // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode - // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, - // so the below is a very efficient way to test for it. As for the 0-0x3F, it's - // excluded from isLetter anyway. - return codePoint <= 0x2AF && Character.isLetter(codePoint); - case AndroidSpellCheckerService.SCRIPT_CYRILLIC: - // All Cyrillic characters are in the 400~52F block. There are some in the upper - // Unicode range, but they are archaic characters that are not used in modern - // Russian and are not used by our dictionary. - return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); - case AndroidSpellCheckerService.SCRIPT_GREEK: - // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the - // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters. - // Our dictionary also contains a few words with 0xF2; it would be best to check - // if that's correct, but a web search does return results for these words so - // they are probably okay. - return (codePoint >= 0x370 && codePoint <= 0x3FF) - || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) - || codePoint == 0xF2; - default: - // Should never come here - throw new RuntimeException("Impossible value of script: " + script); - } - } - private static final int CHECKABILITY_CHECKABLE = 0; private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1; private static final int CHECKABILITY_CONTAINS_PERIOD = 2; @@ -189,7 +152,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { // Filter by first letter final int firstCodePoint = text.codePointAt(0); // Filter out words that don't start with a letter or an apostrophe - if (!isLetterCheckableByLanguage(firstCodePoint, script) + if (!ScriptUtils.isLetterCheckableByScript(firstCodePoint, script) && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE; // Filter contents @@ -210,7 +173,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { if (Constants.CODE_PERIOD == codePoint) { return CHECKABILITY_CONTAINS_PERIOD; } - if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; + if (ScriptUtils.isLetterCheckableByScript(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters // in this word are letters @@ -295,7 +258,9 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS, new String[] { - TextUtils.join(Constants.STRING_SPACE, splitText) }); + TextUtils.join(Constants.STRING_SPACE, splitText), + TextUtils.join(Constants.STRING_PERIOD_AND_SPACE, + splitText) }); } } return dictInfo.mDictionary.isValidWord(inText) diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java index 4248bebf6..9ec19efa8 100644 --- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java +++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java @@ -117,7 +117,8 @@ public final class LanguageModelParam { continue; } languageModelParams.add(languageModelParam); - prevWordsInfo = new PrevWordsInfo(languageModelParam.mTargetWord); + prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo( + new PrevWordsInfo.WordInfo(tempWord)); } return languageModelParams; } @@ -153,7 +154,7 @@ public final class LanguageModelParam { final DistracterFilter distracterFilter) { final String word; if (StringUtils.getCapitalizationType(targetWord) == StringUtils.CAPITALIZE_FIRST - && prevWordsInfo.mPrevWord == null && !isValidWord) { + && !prevWordsInfo.isValid() && !isValidWord) { word = targetWord.toLowerCase(locale); } else { word = targetWord; @@ -167,7 +168,7 @@ public final class LanguageModelParam { } final int unigramProbability = isValidWord ? UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD; - if (prevWordsInfo.mPrevWord == null) { + if (!prevWordsInfo.isValid()) { if (DEBUG) { Log.d(TAG, "--- add unigram: current(" + (isValidWord ? "Valid" : "OOV") + ") = " + word); @@ -175,12 +176,12 @@ public final class LanguageModelParam { return new LanguageModelParam(word, unigramProbability, timestamp); } if (DEBUG) { - Log.d(TAG, "--- add bigram: prev = " + prevWordsInfo.mPrevWord + ", current(" + Log.d(TAG, "--- add bigram: prev = " + prevWordsInfo + ", current(" + (isValidWord ? "Valid" : "OOV") + ") = " + word); } final int bigramProbability = isValidWord ? BIGRAM_PROBABILITY_FOR_VALID_WORD : BIGRAM_PROBABILITY_FOR_OOV_WORD; - return new LanguageModelParam(prevWordsInfo.mPrevWord, word, unigramProbability, - bigramProbability, timestamp); + return new LanguageModelParam(prevWordsInfo.mPrevWordsInfo[0].mWord, word, + unigramProbability, bigramProbability, timestamp); } } diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java new file mode 100644 index 000000000..4dfb38d80 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012 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.latin.utils; + +import java.util.Locale; +import java.util.TreeMap; + +/** + * A class to help with handling different writing scripts. + */ +public class ScriptUtils { + public static final int SCRIPT_LATIN = 0; + public static final int SCRIPT_CYRILLIC = 1; + public static final int SCRIPT_GREEK = 2; + public static final TreeMap<String, Integer> mLanguageToScript; + static { + // List of the supported languages and their associated script. We won't check + // words written in another script than the selected script, because we know we + // don't have those in our dictionary so we will underline everything and we + // will never have any suggestions, so it makes no sense checking them, and this + // is done in {@link #shouldFilterOut}. Also, the script is used to choose which + // proximity to pass to the dictionary descent algorithm. + // IMPORTANT: this only contains languages - do not write countries in there. + // Only the language is searched from the map. + mLanguageToScript = new TreeMap<>(); + mLanguageToScript.put("cs", SCRIPT_LATIN); + mLanguageToScript.put("da", SCRIPT_LATIN); + mLanguageToScript.put("de", SCRIPT_LATIN); + mLanguageToScript.put("el", SCRIPT_GREEK); + mLanguageToScript.put("en", SCRIPT_LATIN); + mLanguageToScript.put("es", SCRIPT_LATIN); + mLanguageToScript.put("fi", SCRIPT_LATIN); + mLanguageToScript.put("fr", SCRIPT_LATIN); + mLanguageToScript.put("hr", SCRIPT_LATIN); + mLanguageToScript.put("it", SCRIPT_LATIN); + mLanguageToScript.put("lt", SCRIPT_LATIN); + mLanguageToScript.put("lv", SCRIPT_LATIN); + mLanguageToScript.put("nb", SCRIPT_LATIN); + mLanguageToScript.put("nl", SCRIPT_LATIN); + mLanguageToScript.put("pt", SCRIPT_LATIN); + mLanguageToScript.put("sl", SCRIPT_LATIN); + mLanguageToScript.put("ru", SCRIPT_CYRILLIC); + } + /* + * Returns whether the code point is a letter that makes sense for the specified + * locale for this spell checker. + * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml + * and is limited to EFIGS languages and Russian. + * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters + * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters. + */ + public static boolean isLetterCheckableByScript(final int codePoint, final int script) { + switch (script) { + case SCRIPT_LATIN: + // Our supported latin script dictionaries (EFIGS) at the moment only include + // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode + // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, + // so the below is a very efficient way to test for it. As for the 0-0x3F, it's + // excluded from isLetter anyway. + return codePoint <= 0x2AF && Character.isLetter(codePoint); + case SCRIPT_CYRILLIC: + // All Cyrillic characters are in the 400~52F block. There are some in the upper + // Unicode range, but they are archaic characters that are not used in modern + // Russian and are not used by our dictionary. + return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); + case SCRIPT_GREEK: + // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the + // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters. + // Our dictionary also contains a few words with 0xF2; it would be best to check + // if that's correct, but a web search does return results for these words so + // they are probably okay. + return (codePoint >= 0x370 && codePoint <= 0x3FF) + || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) + || codePoint == 0xF2; + default: + // Should never come here + throw new RuntimeException("Impossible value of script: " + script); + } + } + + public static int getScriptFromLocale(final Locale locale) { + final Integer script = mLanguageToScript.get(locale.getLanguage()); + if (null == script) { + throw new RuntimeException("We have been called with an unsupported language: \"" + + locale.getLanguage() + "\". Framework bug?"); + } + return script; + } +} |