diff options
115 files changed, 946 insertions, 847 deletions
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java index 01f5e1079..f7ba693af 100644 --- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java +++ b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.common; import java.util.ArrayList; import java.util.Collection; diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java index 3a9705904..031662411 100644 --- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java +++ b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.common; import javax.annotation.Nonnull; diff --git a/java/src/com/android/inputmethod/latin/utils/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java index f1106a6c6..676845842 100644 --- a/java/src/com/android/inputmethod/latin/utils/FileUtils.java +++ b/common/src/com/android/inputmethod/latin/common/FileUtils.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.common; import java.io.File; import java.io.FilenameFilter; diff --git a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java index 4f0805c5c..14b3d220d 100644 --- a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java +++ b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java @@ -14,11 +14,7 @@ * the License. */ -package com.android.inputmethod.dictionarypack; - -import android.content.res.Configuration; -import android.content.res.Resources; -import android.text.TextUtils; +package com.android.inputmethod.latin.common; import java.util.HashMap; import java.util.Locale; @@ -105,8 +101,8 @@ public final class LocaleUtils { * @return a constant that measures how well the tested locale matches the reference locale. */ public static int getMatchLevel(final String referenceLocale, final String testedLocale) { - if (TextUtils.isEmpty(referenceLocale)) { - return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH; + if (StringUtils.isEmpty(referenceLocale)) { + return StringUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH; } if (null == testedLocale) return LOCALE_NO_MATCH; final String[] referenceParams = referenceLocale.split("_", 3); @@ -160,21 +156,6 @@ public final class LocaleUtils { return LOCALE_MATCH <= level; } - /** - * Sets the system locale for this process. - * - * @param res the resources to use. Pass current resources. - * @param newLocale the locale to change to. - * @return the old locale. - */ - public static Locale setSystemLocale(final Resources res, final Locale newLocale) { - final Configuration conf = res.getConfiguration(); - final Locale saveLocale = conf.locale; - conf.locale = newLocale; - res.updateConfiguration(conf, res.getDisplayMetrics()); - return saveLocale; - } - private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); /** diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java index 7603dbba5..7ef741cc2 100644 --- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java +++ b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin.settings; +package com.android.inputmethod.latin.common; public class NativeSuggestOptions { // Need to update suggest_options.h when you add, remove or reorder options. @@ -25,8 +25,11 @@ public class NativeSuggestOptions { private static final int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4; private static final int OPTIONS_SIZE = 5; - private final int[] mOptions = new int[OPTIONS_SIZE - + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE]; + private final int[] mOptions; + + public NativeSuggestOptions(final int additionalFeaturesSettingsSize) { + mOptions = new int[OPTIONS_SIZE + additionalFeaturesSettingsSize]; + } public void setIsGesture(final boolean value) { setBooleanOption(IS_GESTURE, value); diff --git a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java index bd54238f8..4e8a10b1f 100644 --- a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java @@ -47,6 +47,7 @@ public class AdditionalFeaturesSettingUtils { // do nothing. } + @Nonnull public static RichInputMethodSubtype createRichInputMethodSubtype( @Nonnull final RichInputMethodManager imm, @Nonnull final InputMethodSubtype subtype, diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index ee1cef6b5..88e867f09 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -37,7 +37,6 @@ <application android:label="@string/english_ime_name" android:icon="@drawable/ic_launcher_keyboard" - android:killAfterRestore="false" android:supportsRtl="true" android:allowBackup="true"> diff --git a/java/res/values-af/strings-emoji-descriptions.xml b/java/res/values-af/strings-emoji-descriptions.xml index 26bfc5f08..5ed066a87 100644 --- a/java/res/values-af/strings-emoji-descriptions.xml +++ b/java/res/values-af/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Koekie"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Staaf sjokolade"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Lekkergoed"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Suiglekker"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vla"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Heuningpot"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Broskoek"</string> diff --git a/java/res/values-ar/strings-emoji-descriptions.xml b/java/res/values-ar/strings-emoji-descriptions.xml index 875f626c2..c1decd303 100644 --- a/java/res/values-ar/strings-emoji-descriptions.xml +++ b/java/res/values-ar/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"كعكة"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"بار شيكولاتة"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"حلوى"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"مصاصة"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"كاسترد"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"جرة عسل"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"كعكة بسمن وسكر"</string> diff --git a/java/res/values-bn-rBD/strings-emoji-descriptions.xml b/java/res/values-bn-rBD/strings-emoji-descriptions.xml index 4c661661d..3c58621f4 100644 --- a/java/res/values-bn-rBD/strings-emoji-descriptions.xml +++ b/java/res/values-bn-rBD/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"কুকি"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"চকোলেট বার"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ক্যান্ডি"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ললিপপ"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"কাস্টার্ড"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"মধুর পাত্র"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"শর্টকেক"</string> diff --git a/java/res/values-cs/strings-emoji-descriptions.xml b/java/res/values-cs/strings-emoji-descriptions.xml index b66420247..b7f359c55 100644 --- a/java/res/values-cs/strings-emoji-descriptions.xml +++ b/java/res/values-cs/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sušenka"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Tabulka čokolády"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bonbon"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lízátko"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Pudink"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hrnek medu"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Dort"</string> diff --git a/java/res/values-el/strings-emoji-descriptions.xml b/java/res/values-el/strings-emoji-descriptions.xml index aa8966e97..a15d0fdba 100644 --- a/java/res/values-el/strings-emoji-descriptions.xml +++ b/java/res/values-el/strings-emoji-descriptions.xml @@ -269,7 +269,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Κούκι"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Σοκολάτα"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Γλυκά"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Γλειφιτζούρι"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Κρέμα"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Βάζο με μέλι"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Μπισκότο"</string> diff --git a/java/res/values-fa/strings-emoji-descriptions.xml b/java/res/values-fa/strings-emoji-descriptions.xml index cc670eef9..8adb530be 100644 --- a/java/res/values-fa/strings-emoji-descriptions.xml +++ b/java/res/values-fa/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"کلوچه"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"تخته شکلات"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"آبنبات"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"آبنبات چوبی"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"آبنبات چوبی"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"کاستارد"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ظرف عسل"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"کیک روغنی"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 2461da5af..16a6f8767 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -84,7 +84,7 @@ <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"برای ذخیره اینجا را لمس کنید"</string> <string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string> <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحهکلید"</string> - <string name="switch_accounts" msgid="3321216593719006162">"جابجایی بین حسابها"</string> + <string name="switch_accounts" msgid="3321216593719006162">"جابهجایی بین حسابها"</string> <string name="no_accounts_selected" msgid="2073821619103904330">"هیچ حسابی انتخاب نشده است"</string> <string name="account_selected" msgid="2846876462199625974">"در حال حاضر در حال استفاده از <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string> <string name="account_select_ok" msgid="9141195141763227797">"تأیید"</string> diff --git a/java/res/values-fi/strings-emoji-descriptions.xml b/java/res/values-fi/strings-emoji-descriptions.xml index 72af3c229..7d6a9f957 100644 --- a/java/res/values-fi/strings-emoji-descriptions.xml +++ b/java/res/values-fi/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Pikkuleipä"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Suklaapatukka"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Karamelli"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Tikkari"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vanukas"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hunajapurkki"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakkuviipale"</string> diff --git a/java/res/values-fr/strings-emoji-descriptions.xml b/java/res/values-fr/strings-emoji-descriptions.xml index b7ad706fc..1f99ee3bc 100644 --- a/java/res/values-fr/strings-emoji-descriptions.xml +++ b/java/res/values-fr/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biscuit"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barre de chocolat"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bonbon"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Sucette"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Crème anglaise"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Pot de miel"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Sablé"</string> diff --git a/java/res/values-gl-rES/strings-emoji-descriptions.xml b/java/res/values-gl-rES/strings-emoji-descriptions.xml index 31eb89bb2..cdb67fa45 100644 --- a/java/res/values-gl-rES/strings-emoji-descriptions.xml +++ b/java/res/values-gl-rES/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Galleta"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barra de chocolate"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Caramelo"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Chupa-chupa"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Crema"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Tarro de mel"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Pastel"</string> diff --git a/java/res/values-hi/strings-emoji-descriptions.xml b/java/res/values-hi/strings-emoji-descriptions.xml index df5fa1e13..1f18e6add 100644 --- a/java/res/values-hi/strings-emoji-descriptions.xml +++ b/java/res/values-hi/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"कुकी"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"चॉकलेट बार"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"कैंडी"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"लॉलीपॉप"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"दही"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"शहद का बर्तन"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"शॉर्टकेक"</string> diff --git a/java/res/values-hu/strings-emoji-descriptions.xml b/java/res/values-hu/strings-emoji-descriptions.xml index 4f3d01c86..b72f29853 100644 --- a/java/res/values-hu/strings-emoji-descriptions.xml +++ b/java/res/values-hu/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sütemény"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Csokoládé"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Cukorka"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Nyalóka"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Sodó"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Mézesbödön"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Tortaszelet"</string> diff --git a/java/res/values-hy-rAM/strings-emoji-descriptions.xml b/java/res/values-hy-rAM/strings-emoji-descriptions.xml index f41f2fc96..dcc718e22 100644 --- a/java/res/values-hy-rAM/strings-emoji-descriptions.xml +++ b/java/res/values-hy-rAM/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Թխվածքաբլիթ"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Շոկոլադի սալիկ"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Կոնֆետ"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Սառնաշաքար կոնֆետ"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Շաքարաքլոր"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Եփովի կրեմ"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Մեղրանոթ"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Տորթի կտոր"</string> diff --git a/java/res/values-iw/strings-emoji-descriptions.xml b/java/res/values-iw/strings-emoji-descriptions.xml index ab31403aa..fc4435a14 100644 --- a/java/res/values-iw/strings-emoji-descriptions.xml +++ b/java/res/values-iw/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"עוגייה"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"חפיסת שוקולד"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"סוכרייה"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"סוכרייה על מקל"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"רפרפת"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"סיר דבש"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"עוגת פירות"</string> diff --git a/java/res/values-kn-rIN/strings-emoji-descriptions.xml b/java/res/values-kn-rIN/strings-emoji-descriptions.xml index 4e6d5ced5..a013c2755 100644 --- a/java/res/values-kn-rIN/strings-emoji-descriptions.xml +++ b/java/res/values-kn-rIN/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ಕುಕಿ"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ಚಾಕೊಲೇಟ್ ಬಾರ್"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ಕ್ಯಾಂಡಿ"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ಲಾಲಿಪಪ್"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"ಕಸ್ಟರ್ಡ್"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ಜೇನಿನ ಮಡಕೆ"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ಶಾರ್ಟ್ಕೇಕ್"</string> diff --git a/java/res/values-ml-rIN/strings-emoji-descriptions.xml b/java/res/values-ml-rIN/strings-emoji-descriptions.xml index ab6509756..a846f31a5 100644 --- a/java/res/values-ml-rIN/strings-emoji-descriptions.xml +++ b/java/res/values-ml-rIN/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"കുക്കി"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ചോക്കലേറ്റ് ബാർ"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"മിഠായി"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ലോലിപോപ്പ്"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"കസ്റ്റാർഡ്"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"തേൻ കുടം"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ഷോർട്ട്കേക്ക്"</string> diff --git a/java/res/values-nb/strings-emoji-descriptions.xml b/java/res/values-nb/strings-emoji-descriptions.xml index fa655f12f..681118833 100644 --- a/java/res/values-nb/strings-emoji-descriptions.xml +++ b/java/res/values-nb/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Kjeks"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Sjokoladeplate"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Godteri"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Kjærlighet på pinne"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Pudding"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honningkrukke"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakestykke"</string> diff --git a/java/res/values-nl/strings-emoji-descriptions.xml b/java/res/values-nl/strings-emoji-descriptions.xml index a02c21fa0..3b298900b 100644 --- a/java/res/values-nl/strings-emoji-descriptions.xml +++ b/java/res/values-nl/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Cookie"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Chocoladereep"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Snoep"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lolly"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vla"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honingpot"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Cake"</string> diff --git a/java/res/values-ro/strings-emoji-descriptions.xml b/java/res/values-ro/strings-emoji-descriptions.xml index f44a0b974..2ac84e5a0 100644 --- a/java/res/values-ro/strings-emoji-descriptions.xml +++ b/java/res/values-ro/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biscuit"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Ciocolată"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bomboane"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Acadea"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Budincă"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Oală de miere"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Prăjitură"</string> diff --git a/java/res/values-sw/strings-emoji-descriptions.xml b/java/res/values-sw/strings-emoji-descriptions.xml index 57163878e..329bf398a 100644 --- a/java/res/values-sw/strings-emoji-descriptions.xml +++ b/java/res/values-sw/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biskuti"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Mchi wa chokoleti"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Peremende"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Peremende ya kijiti"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Faluda au Kastadi"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Emoji ya chungu cha asali"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Keki tamu yenye vitandamlo inayofanana na biskuti"</string> diff --git a/java/res/values-th/strings-emoji-descriptions.xml b/java/res/values-th/strings-emoji-descriptions.xml index e5ef9b89d..86ab2c08e 100644 --- a/java/res/values-th/strings-emoji-descriptions.xml +++ b/java/res/values-th/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"คุกกี้"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ช็อกโกแลตแท่ง"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ลูกกวาด"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"อมยิ้ม"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"คัสตาร์ด"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"โถน้ำผึ้ง"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ชอร์ตเค้ก"</string> diff --git a/java/res/values-ur-rPK/strings-emoji-descriptions.xml b/java/res/values-ur-rPK/strings-emoji-descriptions.xml index d5bcf2f94..e6bbdcf49 100644 --- a/java/res/values-ur-rPK/strings-emoji-descriptions.xml +++ b/java/res/values-ur-rPK/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"کوکی"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"چاکلیٹ بار"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"قندی"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"لالی پاپ"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"کسٹرڈ"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"شہد کا برتن"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"کیک کا ٹکڑا"</string> diff --git a/java/res/values-vi/strings-emoji-descriptions.xml b/java/res/values-vi/strings-emoji-descriptions.xml index 8b44dcfbf..492c726d1 100644 --- a/java/res/values-vi/strings-emoji-descriptions.xml +++ b/java/res/values-vi/strings-emoji-descriptions.xml @@ -267,7 +267,7 @@ <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Bánh quy"</string> <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Thanh sôcôla"</string> <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Kẹo"</string> - <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Kẹo que"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string> <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Món sữa trứng"</string> <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Mắt ong"</string> <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Bánh bơ giòn"</string> diff --git a/java/res/xml-sw600dp/key_space_3kw.xml b/java/res/xml-sw600dp/key_space_3kw.xml index 9932d342e..8cc3a38a5 100644 --- a/java/res/xml-sw600dp/key_space_3kw.xml +++ b/java/res/xml-sw600dp/key_space_3kw.xml @@ -22,12 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> - <!-- fa: Perisan - kn: Kannada - ne: Nepali - te: Telugu --> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="true" > <Key @@ -39,7 +35,7 @@ latin:keyStyle="zwnjKeyStyle" /> </case> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="false" > <Key diff --git a/java/res/xml-sw600dp/key_space_7kw.xml b/java/res/xml-sw600dp/key_space_7kw.xml index 3311f812a..61e076534 100644 --- a/java/res/xml-sw600dp/key_space_7kw.xml +++ b/java/res/xml-sw600dp/key_space_7kw.xml @@ -22,12 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> - <!-- fa: Perisan - kn: Kannada - ne: Nepali - te: Telugu --> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="true" > <Key @@ -39,7 +35,7 @@ latin:keyStyle="zwnjKeyStyle" /> </case> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="false" > <Key diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml index 7a4700d5a..e6fdf73ec 100644 --- a/java/res/xml-sw600dp/rows_number_normal.xml +++ b/java/res/xml-sw600dp/rows_number_normal.xml @@ -24,17 +24,17 @@ <Row> <Key latin:keySpec="-" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="+" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="." - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -53,13 +53,15 @@ latin:keyWidth="fillRight" /> </Row> <Row> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="/" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <switch> @@ -70,14 +72,14 @@ latin:keySpec="," latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/morekeys_am_pm" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </case> <default> <Key latin:keySpec="," - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </default> @@ -100,12 +102,12 @@ <Row> <Key latin:keySpec="(" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec=")" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <switch> @@ -114,14 +116,14 @@ > <Key latin:keySpec=":" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </case> <default> <Key latin:keySpec="=" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> </default> @@ -143,8 +145,10 @@ <Key latin:keyStyle="tabletNumSpaceKeyStyle" latin:keyWidth="30%p" /> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" latin:keyXPos="31%p" /> <Key latin:keySpec="0" diff --git a/java/res/xml-sw600dp/rows_number_password.xml b/java/res/xml-sw600dp/rows_number_password.xml index 6c3855a01..37e63383e 100644 --- a/java/res/xml-sw600dp/rows_number_password.xml +++ b/java/res/xml-sw600dp/rows_number_password.xml @@ -70,7 +70,8 @@ <Key latin:keyStyle="deleteKeyStyle" /> <Key - latin:keyStyle="num0KeyStyle" /> + latin:keyStyle="num0KeyStyle" + latin:keyHintLabel="+" /> <Key latin:keyStyle="enterKeyStyle" /> <!-- Note: This Spacer prevents the above key from being marked as a right edge key. --> diff --git a/java/res/xml-sw600dp/rows_phone.xml b/java/res/xml-sw600dp/rows_phone.xml index 612397a90..fc86a7670 100644 --- a/java/res/xml-sw600dp/rows_phone.xml +++ b/java/res/xml-sw600dp/rows_phone.xml @@ -28,16 +28,18 @@ <Row> <Key latin:keySpec="-" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="+" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key - latin:keyStyle="numPauseKeyStyle" + latin:keySpec="!string/label_pause_key|," + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -55,16 +57,18 @@ <Row> <Key latin:keySpec="," - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="." - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key - latin:keyStyle="numWaitKeyStyle" + latin:keySpec="!string/label_wait_key|;" + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -82,17 +86,17 @@ <Row> <Key latin:keySpec="(" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec=")" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key latin:keySpec="N" - latin:keyStyle="numKeyStyle" + latin:keyStyle="numSymbolKeyStyle" latin:keyWidth="10%p" latin:backgroundType="functional" /> <Key @@ -109,13 +113,16 @@ <Key latin:keyStyle="tabletNumSpaceKeyStyle" latin:keyWidth="30%p" /> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" latin:keyXPos="31%p" /> <Key - latin:keyStyle="num0KeyStyle" /> + latin:keyStyle="num0KeyStyle" + latin:keyHintLabel="+" /> <Key latin:keySpec="\#" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> </Row> </merge> diff --git a/java/res/xml/kbd_emoji_category1.xml b/java/res/xml/kbd_emoji_category1.xml index 5145ea9d3..2770cfb15 100644 --- a/java/res/xml/kbd_emoji_category1.xml +++ b/java/res/xml/kbd_emoji_category1.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_faces" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category2.xml b/java/res/xml/kbd_emoji_category2.xml index ac8784f4b..d547056e1 100644 --- a/java/res/xml/kbd_emoji_category2.xml +++ b/java/res/xml/kbd_emoji_category2.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_objects" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category3.xml b/java/res/xml/kbd_emoji_category3.xml index 88c4db92b..2172d9880 100644 --- a/java/res/xml/kbd_emoji_category3.xml +++ b/java/res/xml/kbd_emoji_category3.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_nature" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category4.xml b/java/res/xml/kbd_emoji_category4.xml index 262384d80..46b6d46e8 100644 --- a/java/res/xml/kbd_emoji_category4.xml +++ b/java/res/xml/kbd_emoji_category4.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_places" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category5.xml b/java/res/xml/kbd_emoji_category5.xml index bf823f978..4304701d4 100644 --- a/java/res/xml/kbd_emoji_category5.xml +++ b/java/res/xml/kbd_emoji_category5.xml @@ -27,5 +27,5 @@ <GridRows latin:codesArray="@array/emoji_symbols" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_category6.xml b/java/res/xml/kbd_emoji_category6.xml index edb82fc64..516ed7a42 100644 --- a/java/res/xml/kbd_emoji_category6.xml +++ b/java/res/xml/kbd_emoji_category6.xml @@ -28,5 +28,5 @@ <GridRows latin:textsArray="@array/emoji_emoticons" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/kbd_emoji_recents.xml b/java/res/xml/kbd_emoji_recents.xml index edf3872c1..4953c1079 100644 --- a/java/res/xml/kbd_emoji_recents.xml +++ b/java/res/xml/kbd_emoji_recents.xml @@ -28,5 +28,5 @@ <GridRows latin:codesArray="@array/emoji_recents" latin:keyLabelFlags="fontNormal" - latin:backgroundType="empty" /> + latin:backgroundType="normal" /> </Keyboard> diff --git a/java/res/xml/key_space_5kw.xml b/java/res/xml/key_space_5kw.xml index b1fe0bbeb..692c245ff 100644 --- a/java/res/xml/key_space_5kw.xml +++ b/java/res/xml/key_space_5kw.xml @@ -22,12 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> - <!-- fa: Perisan - kn: Kannada - ne: Nepali - te: Telugu --> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="true" > <Key @@ -39,7 +35,7 @@ latin:keyStyle="zwnjKeyStyle" /> </case> <case - latin:languageCode="fa|kn|ne|te" + latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu" latin:languageSwitchKeyEnabled="false" > <Key diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml index 847b43610..911c2763d 100644 --- a/java/res/xml/key_styles_number.xml +++ b/java/res/xml/key_styles_number.xml @@ -33,9 +33,7 @@ latin:keyLabelFlags="fontNormal|followKeyLetterRatio|followFunctionalTextColor" latin:parentStyle="numKeyBaseStyle" /> <key-style - latin:styleName="numFunctionalKeyStyle" - latin:keyLabelFlags="followKeyLargeLetterRatio" - latin:backgroundType="functional" + latin:styleName="numSymbolKeyStyle" latin:parentStyle="numKeyBaseStyle" /> <key-style latin:styleName="numberKeyStyle" @@ -44,7 +42,6 @@ <key-style latin:styleName="num0KeyStyle" latin:keySpec="0" - latin:keyHintLabel="+" latin:parentStyle="numberKeyStyle" /> <key-style latin:styleName="num1KeyStyle" @@ -90,11 +87,6 @@ latin:keySpec="9" latin:keyHintLabel="WXYZ" latin:parentStyle="numberKeyStyle" /> - <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> - <key-style - latin:styleName="numStarKeyStyle" - latin:keySpec="*|*" - latin:parentStyle="numKeyStyle" /> <!-- Only for non-tablet device --> <key-style latin:styleName="numPhoneToSymbolKeyStyle" @@ -105,16 +97,6 @@ latin:keySpec="!text/keylabel_to_phone_numeric|!code/key_switch_alpha_symbol" latin:parentStyle="numModeKeyStyle" /> <key-style - latin:styleName="numPauseKeyStyle" - latin:keySpec="!text/label_pause_key|," - latin:keyLabelFlags="followKeyHintLabelRatio|autoXScale" - latin:parentStyle="numKeyBaseStyle" /> - <key-style - latin:styleName="numWaitKeyStyle" - latin:keySpec="!text/label_wait_key|;" - latin:keyLabelFlags="followKeyHintLabelRatio|autoXScale" - latin:parentStyle="numKeyBaseStyle" /> - <key-style latin:styleName="numTabKeyStyle" latin:keyActionFlags="noKeyPreview" latin:parentStyle="tabKeyStyle" /> diff --git a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml index b2b09b22d..267064d46 100644 --- a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml +++ b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml @@ -19,7 +19,7 @@ --> <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Feature latin:supportedScript="devanagari" /> + <Feature latin:supportedScript="bengali" /> <Element latin:elementKeyboard="@xml/kbd_bengali_akkhor" latin:elementName="alphabet" diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml index d8d15080e..0f92ac605 100644 --- a/java/res/xml/rows_number_normal.xml +++ b/java/res/xml/rows_number_normal.xml @@ -35,7 +35,8 @@ latin:keySpec="-" latin:moreKeys="+" latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> @@ -54,7 +55,8 @@ > <Key latin:keySpec="." - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </case> <case @@ -62,15 +64,17 @@ > <Key latin:keySpec="." - latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/morekeys_am_pm" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyLabelFlags="hasPopupHint" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </case> <default> <Key latin:keySpec="," - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </default> </switch> diff --git a/java/res/xml/rows_number_password.xml b/java/res/xml/rows_number_password.xml index 2e61a08ae..65736c430 100644 --- a/java/res/xml/rows_number_password.xml +++ b/java/res/xml/rows_number_password.xml @@ -70,7 +70,8 @@ <Key latin:keyStyle="deleteKeyStyle" /> <Key - latin:keyStyle="num0KeyStyle" /> + latin:keyStyle="num0KeyStyle" + latin:keyHintLabel="+" /> <Key latin:keyStyle="enterKeyStyle" /> <!-- Note: This Spacer prevents the above key from being marked as a right edge key. --> diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml index 03e45419a..bb5590d4e 100644 --- a/java/res/xml/rows_phone.xml +++ b/java/res/xml/rows_phone.xml @@ -36,7 +36,8 @@ latin:keySpec="-" latin:moreKeys="+" latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> @@ -48,7 +49,8 @@ latin:keyStyle="num6KeyStyle" /> <Key latin:keySpec="." - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml index 983bfb5c8..195a183a3 100644 --- a/java/res/xml/rows_phone_symbols.xml +++ b/java/res/xml/rows_phone_symbols.xml @@ -28,45 +28,53 @@ <Row> <Key latin:keySpec="(" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="/" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec=")" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="-" latin:moreKeys="+" latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> <Key latin:keySpec="N" - latin:keyStyle="numKeyBaseStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <!-- Pause is a comma. Check PhoneNumberUtils.java to see if this has changed. --> <Key - latin:keyStyle="numPauseKeyStyle" /> + latin:keySpec="!string/label_pause_key|," + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="," - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="." - latin:keyStyle="numFunctionalKeyStyle" + latin:keyStyle="numKeyStyle" + latin:backgroundType="functional" latin:keyWidth="fillRight" /> </Row> <Row> + <!-- U+FF0A: "*" FULLWIDTH ASTERISK --> <Key - latin:keyStyle="numStarKeyStyle" /> + latin:keySpec="*|*" + latin:keyStyle="numSymbolKeyStyle" /> <!-- Wait is a semicolon. --> <Key - latin:keyStyle="numWaitKeyStyle" /> + latin:keySpec="!string/label_wait_key|;" + latin:keyLabelFlags="followKeyLabelRatio|autoXScale" + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keySpec="\#" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keyStyle="deleteKeyStyle" latin:keyWidth="fillRight" /> @@ -76,7 +84,7 @@ latin:keyStyle="numPhoneToNumericKeyStyle" /> <Key latin:keySpec="+" - latin:keyStyle="numKeyStyle" /> + latin:keyStyle="numSymbolKeyStyle" /> <Key latin:keyStyle="numSpaceKeyStyle" /> <Key diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java index 7fc1e9d8a..2de71cec9 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java +++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java @@ -31,9 +31,9 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; -import com.android.inputmethod.latin.utils.CoordinateUtils; import java.util.List; diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java index 3a27c5739..58ad4bd4c 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java @@ -26,6 +26,8 @@ import com.android.inputmethod.latin.common.Constants; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import javax.annotation.Nonnull; + public final class InputMethodSubtypeCompatUtils { private static final String TAG = InputMethodSubtypeCompatUtils.class.getSimpleName(); // Note that InputMethodSubtype(int nameId, int iconId, String locale, String mode, @@ -53,6 +55,7 @@ public final class InputMethodSubtypeCompatUtils { } @SuppressWarnings("deprecation") + @Nonnull public static InputMethodSubtype newInputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) { diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index d3e24e342..be0744393 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -27,8 +27,8 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.LocaleUtils; import java.lang.reflect.Field; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java index 37fa76be7..659fe5c51 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java @@ -31,6 +31,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; import java.io.File; diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java index e9b634eec..e6acb8f36 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java @@ -25,6 +25,7 @@ import android.os.IBinder; import android.widget.Toast; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.LocaleUtils; import java.util.Locale; import java.util.Random; diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index c2dc87900..14e005007 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -16,6 +16,8 @@ package com.android.inputmethod.dictionarypack; +import com.android.inputmethod.latin.common.LocaleUtils; + import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ContentResolver; diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java index f1633ff28..50b3c72f3 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java +++ b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java @@ -26,6 +26,7 @@ import android.widget.TextView; import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.LocaleUtils; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index d59b7a545..bd6152119 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -39,6 +39,7 @@ import com.android.inputmethod.compat.ConnectivityManagerCompatUtils; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.compat.NotificationCompatUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 3c90a04db..619b801f4 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -22,7 +22,7 @@ import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; import java.util.ArrayList; import java.util.Collections; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 7eb91b588..51f37fdc6 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -38,7 +38,6 @@ import com.android.inputmethod.keyboard.internal.KeysCache; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; -import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.ScriptUtils; @@ -52,6 +51,9 @@ import java.io.IOException; import java.lang.ref.SoftReference; import java.util.HashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * This class represents a set of keyboard layouts. Each of them represents a different keyboard * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same @@ -83,6 +85,8 @@ public final class KeyboardLayoutSet { private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache = new HashMap<>(); private static final KeysCache sKeysCache = new KeysCache(); + private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes = + new HashMap<>(); @SuppressWarnings("serial") public static final class KeyboardLayoutSetException extends RuntimeException { @@ -141,6 +145,16 @@ public final class KeyboardLayoutSet { sKeysCache.clear(); } + public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) { + final Integer value = sScriptIdsForSubtypes.get(subtype); + if (null == value) { + final int scriptId = Builder.readScriptId(resources, subtype); + sScriptIdsForSubtypes.put(subtype, scriptId); + return scriptId; + } + return value; + } + KeyboardLayoutSet(final Context context, final Params params) { mContext = context; mParams = params; @@ -245,7 +259,7 @@ public final class KeyboardLayoutSet { private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo(); - public Builder(final Context context, final EditorInfo ei) { + public Builder(final Context context, @Nullable final EditorInfo ei) { mContext = context; mPackageName = context.getPackageName(); mResources = context.getResources(); @@ -266,7 +280,7 @@ public final class KeyboardLayoutSet { return this; } - public Builder setSubtype(final RichInputMethodSubtype subtype) { + public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) { final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype); // TODO: Consolidate with {@link InputAttributes}. @SuppressWarnings("deprecation") @@ -276,7 +290,7 @@ public final class KeyboardLayoutSet { mParams.mEditorInfo.imeOptions) || deprecatedForceAscii; final RichInputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable) - ? SubtypeSwitcher.getInstance().getNoLanguageSubtype() + ? RichInputMethodSubtype.getNoLanguageSubtype() : subtype; mParams.mSubtype = keyboardSubtype; mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX @@ -304,31 +318,13 @@ public final class KeyboardLayoutSet { return this; } - public Builder setScriptId(final int scriptId) { - mParams.mScriptId = scriptId; - return this; - } - public Builder setSplitLayoutEnabledByUser(final boolean enabled) { mParams.mIsSplitLayoutEnabledByUser = enabled; return this; } - private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes = - new HashMap<>(); - public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) { - final Integer value = sScriptIdsForSubtypes.get(subtype); - if (null == value) { - final int scriptId = readScriptId(resources, subtype); - sScriptIdsForSubtypes.put(subtype, scriptId); - return scriptId; - } - return value; - } - // Super redux version of reading the script ID for some subtype from Xml. - private static int readScriptId(final Resources resources, - final InputMethodSubtype subtype) { + static int readScriptId(final Resources resources, final InputMethodSubtype subtype) { final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); final int xmlId = getXmlId(resources, layoutSetName); @@ -415,7 +411,7 @@ public final class KeyboardLayoutSet { if (TAG_ELEMENT.equals(tag)) { parseKeyboardLayoutSetElement(parser); } else if (TAG_FEATURE.equals(tag)) { - parseKeyboardLayoutSetFeature(parser); + mParams.mScriptId = readScriptIdFromTagFeature(mResources, parser); } else { throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET); } @@ -460,12 +456,6 @@ public final class KeyboardLayoutSet { } } - private void parseKeyboardLayoutSetFeature(final XmlPullParser parser) - throws XmlPullParserException, IOException { - final int scriptId = readScriptIdFromTagFeature(mResources, parser); - setScriptId(scriptId); - } - private static int getKeyboardMode(final EditorInfo editorInfo) { final int inputType = editorInfo.inputType; final int variation = inputType & InputType.TYPE_MASK_VARIATION; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index af24ac48c..5e3a5f17c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -114,7 +114,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); - builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); + builder.setSubtype(RichInputMethodManager.getInstance().getCurrentSubtype()); builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey()); builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED @@ -123,7 +123,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { try { mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState); // TODO: revisit this for multi-lingual input - mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocales()[0], + mKeyboardTextsSet.setLocale( + RichInputMethodManager.getInstance().getCurrentSubtypeLocales()[0], mThemeContext); } catch (KeyboardLayoutSetException e) { Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); @@ -163,7 +164,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { currentSettingsValues.mKeyPreviewDismissEndXScale, currentSettingsValues.mKeyPreviewDismissEndYScale, currentSettingsValues.mKeyPreviewDismissDuration); - keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); + keyboardView.updateShortcutKey(RichInputMethodManager.getInstance().isShortcutImeReady()); final boolean subtypeChanged = (oldKeyboard == null) || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype); final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType( @@ -258,8 +259,12 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } private void setMainKeyboardFrame(final SettingsValues settingsValues) { - mMainKeyboardFrame.setVisibility( - settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE); + final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE; + mKeyboardView.setVisibility(visibility); + // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. + // @see #getVisibleKeyboardView() and + // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets) + mMainKeyboardFrame.setVisibility(visibility); mEmojiPalettesView.setVisibility(View.GONE); mEmojiPalettesView.stopEmojiPalettes(); } @@ -272,6 +277,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); mMainKeyboardFrame.setVisibility(View.GONE); + // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. + // @see #getVisibleKeyboardView() and + // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets) + mKeyboardView.setVisibility(View.GONE); mEmojiPalettesView.startEmojiPalettes( mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL), mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet); @@ -406,9 +415,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } public void onNetworkStateChanged() { - if (mKeyboardView != null) { - mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); + if (mKeyboardView == null) { + return; } + mKeyboardView.updateShortcutKey(RichInputMethodManager.getInstance().isShortcutImeReady()); } public int getKeyboardShiftMode() { diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 1bad7cbb6..cba7ff2a2 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -56,9 +56,9 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; -import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 01522536f..3acc11b59 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -31,7 +31,7 @@ import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelega import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; /** * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 41eb87f81..7902ce852 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -35,10 +35,10 @@ import com.android.inputmethod.keyboard.internal.TimerProxy; import com.android.inputmethod.keyboard.internal.TypingTimeRecorder; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java index 54d3e3b88..09313f811 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java @@ -148,7 +148,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements void callListenerOnPressKey(final Key pressedKey) { mPendingKeyDown = null; - pressedKey.onReleased(); + pressedKey.onPressed(); invalidateKey(pressedKey); mListener.onPressKey(pressedKey); } diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java index 06184f8d2..cf4dd3db3 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java @@ -48,7 +48,7 @@ import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SubtypeSwitcher; +import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.ResourceUtils; @@ -113,7 +113,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange context, null /* editorInfo */); final Resources res = context.getResources(); mEmojiLayoutParams = new EmojiLayoutParams(res); - builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); + builder.setSubtype(RichInputMethodSubtype.getEmojiSubtype()); builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), mEmojiLayoutParams.mEmojiKeyboardHeight); final KeyboardLayoutSet layoutSet = builder.build(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java index a5d47adb3..9c0d7436b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java @@ -24,7 +24,7 @@ import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.widget.RelativeLayout; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; import java.util.ArrayList; diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java index 330ec52f0..5443c2a8c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java @@ -27,7 +27,7 @@ import android.text.TextUtils; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; import javax.annotation.Nonnull; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java index d3764877c..448f1b4b1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java @@ -23,7 +23,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayDeque; diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java index a0bb406aa..b1a3887d8 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java @@ -21,10 +21,10 @@ import android.util.SparseIntArray; import com.android.inputmethod.compat.CharacterCompat; import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.latin.common.CollectionUtils; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java index 3a9aa81a3..8a375c620 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java +++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java @@ -22,7 +22,7 @@ import android.view.MotionEvent; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; public final class NonDistinctMultitouchHelper { private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java index ef4c74d61..73a6f9516 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java @@ -23,7 +23,7 @@ import android.graphics.Path; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.common.CoordinateUtils; /** * Draw rubber band preview graphics during sliding key input. diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java index fd6c24dfe..923f43cbe 100644 --- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java +++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java @@ -16,7 +16,7 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.utils.FileUtils; +import com.android.inputmethod.latin.common.FileUtils; import java.io.File; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index b5d0b446f..46cd3b8b2 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -24,6 +24,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.FileUtils; import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; @@ -33,7 +34,6 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 1570bdae0..46c8d5562 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -21,11 +21,11 @@ import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.util.Log; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; import java.io.File; import java.io.IOException; diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 95390aa9f..aefefd305 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -17,7 +17,8 @@ package com.android.inputmethod.latin; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.settings.NativeSuggestOptions; +import com.android.inputmethod.latin.common.NativeSuggestOptions; +import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils; import com.android.inputmethod.latin.utils.JniUtils; import java.util.Locale; @@ -43,7 +44,8 @@ public final class DicTraverseSession { public final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; public final float[] mInputOutputWeightOfLangModelVsSpatialModel = new float[1]; - public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); + public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions( + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE); private static native long setDicTraverseSessionNative(String locale, long dictSize); private static native void initDicTraverseSessionNative(long nativeDicTraverseSession, diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index b47eaa9bb..d9d22e0fc 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -23,6 +23,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.FileUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; @@ -32,7 +33,6 @@ import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CombinedFormatUtils; import com.android.inputmethod.latin.utils.DistracterFilter; import com.android.inputmethod.latin.utils.ExecutorUtils; -import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index cd09bf6c7..719656b07 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -77,6 +77,7 @@ import com.android.inputmethod.keyboard.TextDecoratorUi; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.define.ProductionFlags; @@ -93,7 +94,6 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; -import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; @@ -603,7 +603,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Has to be package-visible for unit tests @UsedForTesting void loadSettings() { - final Locale[] locales = mSubtypeSwitcher.getCurrentSubtypeLocales(); + final Locale[] locales = mRichImm.getCurrentSubtypeLocales(); final EditorInfo editorInfo = getCurrentInputEditorInfo(); final InputAttributes inputAttributes = new InputAttributes( editorInfo, isFullscreenMode(), getPackageName()); @@ -626,7 +626,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void refreshPersonalizationDictionarySession( final SettingsValues currentSettingsValues) { mDictionaryFacilitator.setIsMonolingualUser( - mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes()); + mRichImm.isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes()); mPersonalizationDictionaryUpdater.onLoadSettings( currentSettingsValues.mUsePersonalizedDicts); mContextualDictionaryUpdater.onLoadSettings(currentSettingsValues.mUsePersonalizedDicts); @@ -657,7 +657,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } void resetDictionaryFacilitatorIfNecessary() { - final Locale[] subtypeSwitcherLocales = mSubtypeSwitcher.getCurrentSubtypeLocales(); + final Locale[] subtypeSwitcherLocales = mRichImm.getCurrentSubtypeLocales(); if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) { return; } @@ -863,7 +863,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // also wouldn't be consuming gesture data. mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER; mRichImm.clearSubtypeCaches(); - mSubtypeSwitcher.refreshSubtypeInfo(); + mSubtypeSwitcher.onSubtypeChanged(mRichImm.getCurrentRawSubtype()); final KeyboardSwitcher switcher = mKeyboardSwitcher; switcher.updateKeyboardTheme(); final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView(); @@ -909,7 +909,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Update to a gesture consumer with the current editor and IME state. mGestureConsumer = GestureConsumer.newInstance(editorInfo, mInputLogic.getPrivateCommandPerformer(), - Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()), + Arrays.asList(mRichImm.getCurrentSubtypeLocales()), switcher.getKeyboard()); // Forward this event to the accessibility utilities, if enabled. @@ -947,7 +947,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // span, so we should reset our state unconditionally, even if restarting is true. // We also tell the input logic about the combining rules for the current subtype, so // it can adjust its combiners if needed. - mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(), + mInputLogic.startInput(mRichImm.getCombiningRulesExtraValueOfCurrentSubtype(), currentSettingsValues); resetDictionaryFacilitatorIfNecessary(); @@ -1077,11 +1077,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } - // This call happens when we have a hardware keyboard as well as when we don't. While we - // don't support hardware keyboards yet we should avoid doing the processing associated - // with cursor movement when we have a hardware keyboard since we are not in charge. + // This call happens whether our view is displayed or not, but if it's not then we should + // not attempt recorrection. This is true even with a hardware keyboard connected: if the + // view is not displayed we have no means of showing suggestions anyway, and if it is then + // we want to show suggestions anyway. final SettingsValues settingsValues = mSettings.getCurrent(); - if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) + if (isInputViewShown() && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), @@ -1194,7 +1195,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) { // If there is a hardware keyboard and a visible software keyboard view has been hidden, // no visual element will be shown on the screen. - outInsets.touchableInsets = inputHeight; + outInsets.contentTopInsets = inputHeight; outInsets.visibleTopInsets = inputHeight; mInsetsUpdater.setInsets(outInsets); return; @@ -1204,7 +1205,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ? mSuggestionStripView.getHeight() : 0; final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight; mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY); - // Need to set touchable region only if a keyboard view is being shown. + // Need to set expanded touchable region only if a keyboard view is being shown. if (visibleKeyboardView.isShown()) { final int touchLeft = 0; final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY; @@ -1423,7 +1424,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // completely replace #onCodeInput. public void onEvent(@Nonnull final Event event) { if (Constants.CODE_SHORTCUT == event.mKeyCode) { - mSubtypeSwitcher.switchToShortcutIME(this); + mRichImm.switchToShortcutIME(this); } final InputTransaction completeInputTransaction = mInputLogic.onCodeInput(mSettings.getCurrent(), event, @@ -1467,7 +1468,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onStartBatchInput() { mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler); mGestureConsumer.onGestureStarted( - Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()), + Arrays.asList(mRichImm.getCurrentSubtypeLocales()), mKeyboardSwitcher.getKeyboard()); } @@ -1589,7 +1590,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // We should clear the contextual strip if there is no suggestion from dictionaries. || noSuggestionsFromDictionaries) { mSuggestionStripView.setSuggestions(suggestedWords, - mSubtypeSwitcher.getCurrentSubtype().isRtlSubtype()); + mRichImm.getCurrentSubtype().isRtlSubtype()); } } @@ -1810,7 +1811,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - mSubtypeSwitcher.onNetworkStateChanged(intent); + mRichImm.onNetworkStateChanged(intent); } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { AudioAndHapticFeedbackManager.getInstance().onRingerModeChanged(); } diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 8d8e7ac38..a1ac55a20 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -17,9 +17,15 @@ package com.android.inputmethod.latin; import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; +import android.inputmethodservice.InputMethodService; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; @@ -28,7 +34,9 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; @@ -36,7 +44,11 @@ import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; @@ -46,6 +58,7 @@ import javax.annotation.Nonnull; // non final for easy mocking. public class RichInputMethodManager { private static final String TAG = RichInputMethodManager.class.getSimpleName(); + private static final boolean DEBUG = false; private RichInputMethodManager() { // This utility class is not publicly instantiable. @@ -56,6 +69,10 @@ public class RichInputMethodManager { private Context mContext; private InputMethodManagerCompatWrapper mImmWrapper; private InputMethodInfoCache mInputMethodInfoCache; + private RichInputMethodSubtype mCurrentRichInputMethodSubtype; + private InputMethodInfo mShortcutInputMethodInfo; + private InputMethodSubtype mShortcutSubtype; + private boolean mIsNetworkConnected; final HashMap<InputMethodInfo, List<InputMethodSubtype>> mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>(); final HashMap<InputMethodInfo, List<InputMethodSubtype>> @@ -95,6 +112,11 @@ public class RichInputMethodManager { SubtypeLocaleUtils.init(context); final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(context); setAdditionalInputMethodSubtypes(additionalSubtypes); + + final ConnectivityManager connectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo info = connectivityManager.getActiveNetworkInfo(); + mIsNetworkConnected = (info != null && info.isConnected()); } public InputMethodSubtype[] getAdditionalSubtypes(final Context context) { @@ -304,10 +326,47 @@ public class RichInputMethodManager { } @Nonnull + public RichInputMethodSubtype onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) { + final RichInputMethodSubtype richSubtype = createCurrentRichInputMethodSubtype(newSubtype); + if (DEBUG) { + Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging()); + } + mCurrentRichInputMethodSubtype = richSubtype; + return richSubtype; + } + + private static RichInputMethodSubtype sForcedSubtypeForTesting = null; + + @UsedForTesting + static void forceSubtype(final InputMethodSubtype subtype) { + sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype); + } + + public Locale[] getCurrentSubtypeLocales() { + if (null != sForcedSubtypeForTesting) { + return sForcedSubtypeForTesting.getLocales(); + } + return getCurrentSubtype().getLocales(); + } + + public RichInputMethodSubtype getCurrentSubtype() { + if (null != sForcedSubtypeForTesting) { + return sForcedSubtypeForTesting; + } + return mCurrentRichInputMethodSubtype; + } + + + public String getCombiningRulesExtraValueOfCurrentSubtype() { + return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype()); + } + + @Nonnull public InputMethodSubtype getCurrentRawSubtype() { return mImmWrapper.mImm.getCurrentInputMethodSubtype(); } + @Nonnull public RichInputMethodSubtype createCurrentRichInputMethodSubtype( @Nonnull final InputMethodSubtype rawSubtype) { return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype, @@ -432,4 +491,121 @@ public class RichInputMethodManager { } return mImmWrapper.shouldOfferSwitchingToNextInputMethod(binder); } + + public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() { + final Locale systemLocale = mContext.getResources().getConfiguration().locale; + final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>(); + final InputMethodManager inputMethodManager = getInputMethodManager(); + final List<InputMethodInfo> enabledInputMethodInfoList = + inputMethodManager.getEnabledInputMethodList(); + for (final InputMethodInfo info : enabledInputMethodInfoList) { + final List<InputMethodSubtype> enabledSubtypes = + inputMethodManager.getEnabledInputMethodSubtypeList( + info, true /* allowsImplicitlySelectedSubtypes */); + if (enabledSubtypes.isEmpty()) { + // An IME with no subtypes is found. + return false; + } + enabledSubtypesOfEnabledImes.addAll(enabledSubtypes); + } + for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) { + if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty() + && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) { + return false; + } + } + return true; + } + + // TODO: Make this private + void updateShortcutIME() { + if (DEBUG) { + Log.d(TAG, "Update shortcut IME from : " + + (mShortcutInputMethodInfo == null + ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " + + (mShortcutSubtype == null ? "<null>" : ( + mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); + } + // TODO: Update an icon for shortcut IME + final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = + getInputMethodManager().getShortcutInputMethodsAndSubtypes(); + mShortcutInputMethodInfo = null; + mShortcutSubtype = null; + for (final InputMethodInfo imi : shortcuts.keySet()) { + final List<InputMethodSubtype> subtypes = shortcuts.get(imi); + // TODO: Returns the first found IMI for now. Should handle all shortcuts as + // appropriate. + mShortcutInputMethodInfo = imi; + // TODO: Pick up the first found subtype for now. Should handle all subtypes + // as appropriate. + mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; + break; + } + if (DEBUG) { + Log.d(TAG, "Update shortcut IME to : " + + (mShortcutInputMethodInfo == null + ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " + + (mShortcutSubtype == null ? "<null>" : ( + mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); + } + } + + public void switchToShortcutIME(final InputMethodService context) { + if (mShortcutInputMethodInfo == null) { + return; + } + + final String imiId = mShortcutInputMethodInfo.getId(); + switchToTargetIME(imiId, mShortcutSubtype, context); + } + + private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype, + final InputMethodService context) { + final IBinder token = context.getWindow().getWindow().getAttributes().token; + if (token == null) { + return; + } + final InputMethodManager imm = getInputMethodManager(); + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + imm.setInputMethodAndSubtype(token, imiId, subtype); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public boolean isShortcutImeEnabled() { + updateShortcutIME(); + if (mShortcutInputMethodInfo == null) { + return false; + } + if (mShortcutSubtype == null) { + return true; + } + return checkIfSubtypeBelongsToImeAndEnabled( + mShortcutInputMethodInfo, mShortcutSubtype); + } + + public boolean isShortcutImeReady() { + updateShortcutIME(); + if (mShortcutInputMethodInfo == null) { + return false; + } + if (mShortcutSubtype == null) { + return true; + } + if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) { + return mIsNetworkConnected; + } + return true; + } + + public void onNetworkStateChanged(final Intent intent) { + final boolean noConnection = intent.getBooleanExtra( + ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); + mIsNetworkConnected = !noConnection; + + KeyboardSwitcher.getInstance().onNetworkStateChanged(); + } } diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java index 8d055531c..03f6d60ab 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java @@ -16,33 +16,45 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE; + +import android.util.Log; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Arrays; import java.util.Locale; +import javax.annotation.Nonnull; + /** * Enrichment class for InputMethodSubtype to enable concurrent multi-lingual input. * * Right now, this returns the extra value of its primary subtype. */ public final class RichInputMethodSubtype { + private static final String TAG = RichInputMethodSubtype.class.getSimpleName(); + + @Nonnull private final InputMethodSubtype mSubtype; + @Nonnull private final Locale[] mLocales; - public RichInputMethodSubtype(final InputMethodSubtype subtype, final Locale... locales) { + public RichInputMethodSubtype(@Nonnull final InputMethodSubtype subtype, + @Nonnull final Locale... locales) { mSubtype = subtype; - mLocales = new Locale[locales.length+1]; + mLocales = new Locale[locales.length + 1]; mLocales[0] = LocaleUtils.constructLocaleFromString(mSubtype.getLocale()); System.arraycopy(locales, 0, mLocales, 1, locales.length); } // Extra values are determined by the primary subtype. This is probably right, but // we may have to revisit this later. - public String getExtraValueOf(final String key) { + public String getExtraValueOf(@Nonnull final String key) { return mSubtype.getExtraValueOf(key); } @@ -80,6 +92,7 @@ public final class RichInputMethodSubtype { // en_US azerty T English English (US) // zz azerty T AZERTY AZERTY // Get the RichInputMethodSubtype's full display name in its locale. + @Nonnull public String getFullDisplayName() { if (isNoLanguage()) { return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype); @@ -88,6 +101,7 @@ public final class RichInputMethodSubtype { } // Get the RichInputMethodSubtype's middle display name in its locale. + @Nonnull public String getMiddleDisplayName() { if (isNoLanguage()) { return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype); @@ -96,7 +110,7 @@ public final class RichInputMethodSubtype { } @Override - public boolean equals(Object o) { + public boolean equals(final Object o) { if (!(o instanceof RichInputMethodSubtype)) { return false; } @@ -114,6 +128,7 @@ public final class RichInputMethodSubtype { return "Multi-lingual subtype: " + mSubtype.toString() + ", " + Arrays.toString(mLocales); } + @Nonnull public Locale[] getLocales() { return mLocales; } @@ -124,5 +139,80 @@ public final class RichInputMethodSubtype { } // TODO: remove this method + @Nonnull public InputMethodSubtype getRawSubtype() { return mSubtype; } + + // Dummy no language QWERTY subtype. See {@link R.xml.method}. + private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3; + private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE = + "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY + + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; + @Nonnull + private static final RichInputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = + new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype( + R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark, + SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE, + EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE, + false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, + SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE)); + // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}. + // Dummy Emoji subtype. See {@link R.xml.method}. + private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0; + private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE = + "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; + @Nonnull + private static final RichInputMethodSubtype DUMMY_EMOJI_SUBTYPE = new RichInputMethodSubtype( + InputMethodSubtypeCompatUtils.newInputMethodSubtype( + R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark, + SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE, + EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE, + false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, + SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE)); + private static RichInputMethodSubtype sNoLanguageSubtype; + private static RichInputMethodSubtype sEmojiSubtype; + + @Nonnull + public static RichInputMethodSubtype getNoLanguageSubtype() { + RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype; + if (noLanguageSubtype == null) { + final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance() + .findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY); + if (rawNoLanguageSubtype != null) { + noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype); + } + } + if (noLanguageSubtype != null) { + sNoLanguageSubtype = noLanguageSubtype; + return noLanguageSubtype; + } + Log.w(TAG, "Can't find any language with QWERTY subtype"); + Log.w(TAG, "No input method subtype found; returning dummy subtype: " + + DUMMY_NO_LANGUAGE_SUBTYPE); + return DUMMY_NO_LANGUAGE_SUBTYPE; + } + + @Nonnull + public static RichInputMethodSubtype getEmojiSubtype() { + RichInputMethodSubtype emojiSubtype = sEmojiSubtype; + if (emojiSubtype == null) { + final InputMethodSubtype rawEmojiSubtype = RichInputMethodManager.getInstance() + .findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI); + if (rawEmojiSubtype != null) { + emojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype); + } + } + if (emojiSubtype != null) { + sEmojiSubtype = emojiSubtype; + return emojiSubtype; + } + Log.w(TAG, "Can't find emoji subtype"); + Log.w(TAG, "No input method subtype found; returning dummy subtype: " + + DUMMY_EMOJI_SUBTYPE); + return DUMMY_EMOJI_SUBTYPE; + } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 98bce95bd..b2766e27a 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -16,40 +16,19 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; -import android.inputmethodservice.InputMethodService; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.AsyncTask; -import android.os.IBinder; -import android.util.Log; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; -import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; -import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; -import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.Set; import javax.annotation.Nonnull; public final class SubtypeSwitcher { - private static boolean DBG = DebugFlags.DEBUG_ENABLED; - private static final String TAG = SubtypeSwitcher.class.getSimpleName(); - private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); private /* final */ RichInputMethodManager mRichImm; @@ -57,41 +36,6 @@ public final class SubtypeSwitcher { private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper = new LanguageOnSpacebarHelper(); - private InputMethodInfo mShortcutInputMethodInfo; - private InputMethodSubtype mShortcutSubtype; - private RichInputMethodSubtype mCurrentRichInputMethodSubtype; - private RichInputMethodSubtype mNoLanguageSubtype; - private RichInputMethodSubtype mEmojiSubtype; - private boolean mIsNetworkConnected; - - private static final String KEYBOARD_MODE = "keyboard"; - // Dummy no language QWERTY subtype. See {@link R.xml.method}. - private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3; - private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE = - "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY - + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE - + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE - + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; - private static final RichInputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = - new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype( - R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark, - SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE, - EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE, - false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, - SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE)); - // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}. - // Dummy Emoji subtype. See {@link R.xml.method}. - private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0; - private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE = - "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI - + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; - private static final RichInputMethodSubtype DUMMY_EMOJI_SUBTYPE = new RichInputMethodSubtype( - InputMethodSubtypeCompatUtils.newInputMethodSubtype( - R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark, - SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE, - EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE, - false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, - SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE)); public static SubtypeSwitcher getInstance() { return sInstance; @@ -113,18 +57,9 @@ public final class SubtypeSwitcher { } mResources = context.getResources(); mRichImm = RichInputMethodManager.getInstance(); - ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( - Context.CONNECTIVITY_SERVICE); - - final NetworkInfo info = connectivityManager.getActiveNetworkInfo(); - mIsNetworkConnected = (info != null && info.isConnected()); - - refreshSubtypeInfo(); - updateParametersOnStartInputView(); - } - public void refreshSubtypeInfo() { onSubtypeChanged(mRichImm.getCurrentRawSubtype()); + updateParametersOnStartInputView(); } /** @@ -135,49 +70,12 @@ public final class SubtypeSwitcher { final List<InputMethodSubtype> enabledSubtypesOfThisIme = mRichImm.getMyEnabledInputMethodSubtypeList(true); mLanguageOnSpacebarHelper.updateEnabledSubtypes(enabledSubtypesOfThisIme); - updateShortcutIME(); - } - - private void updateShortcutIME() { - if (DBG) { - Log.d(TAG, "Update shortcut IME from : " - + (mShortcutInputMethodInfo == null - ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " - + (mShortcutSubtype == null ? "<null>" : ( - mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); - } - // TODO: Update an icon for shortcut IME - final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = - mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes(); - mShortcutInputMethodInfo = null; - mShortcutSubtype = null; - for (final InputMethodInfo imi : shortcuts.keySet()) { - final List<InputMethodSubtype> subtypes = shortcuts.get(imi); - // TODO: Returns the first found IMI for now. Should handle all shortcuts as - // appropriate. - mShortcutInputMethodInfo = imi; - // TODO: Pick up the first found subtype for now. Should handle all subtypes - // as appropriate. - mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null; - break; - } - if (DBG) { - Log.d(TAG, "Update shortcut IME to : " - + (mShortcutInputMethodInfo == null - ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " - + (mShortcutSubtype == null ? "<null>" : ( - mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode()))); - } + mRichImm.updateShortcutIME(); } // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) { - final RichInputMethodSubtype richSubtype = - mRichImm.createCurrentRichInputMethodSubtype(newSubtype); - if (DBG) { - Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging()); - } - mCurrentRichInputMethodSubtype = richSubtype; + final RichInputMethodSubtype richSubtype = mRichImm.onSubtypeChanged(newSubtype); final Locale[] newLocales = richSubtype.getLocales(); if (newLocales.length > 1) { // In multi-locales mode, the system language is never the same as the input language @@ -193,160 +91,10 @@ public final class SubtypeSwitcher { mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage( sameLocale || (sameLanguage && implicitlyEnabled)); } - updateShortcutIME(); - } - - //////////////////////////// - // Shortcut IME functions // - //////////////////////////// - - public void switchToShortcutIME(final InputMethodService context) { - if (mShortcutInputMethodInfo == null) { - return; - } - - final String imiId = mShortcutInputMethodInfo.getId(); - switchToTargetIME(imiId, mShortcutSubtype, context); - } - - private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype, - final InputMethodService context) { - final IBinder token = context.getWindow().getWindow().getAttributes().token; - if (token == null) { - return; - } - final InputMethodManager imm = mRichImm.getInputMethodManager(); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - imm.setInputMethodAndSubtype(token, imiId, subtype); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + mRichImm.updateShortcutIME(); } - public boolean isShortcutImeEnabled() { - updateShortcutIME(); - if (mShortcutInputMethodInfo == null) { - return false; - } - if (mShortcutSubtype == null) { - return true; - } - return mRichImm.checkIfSubtypeBelongsToImeAndEnabled( - mShortcutInputMethodInfo, mShortcutSubtype); - } - - public boolean isShortcutImeReady() { - updateShortcutIME(); - if (mShortcutInputMethodInfo == null) { - return false; - } - if (mShortcutSubtype == null) { - return true; - } - if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) { - return mIsNetworkConnected; - } - return true; - } - - public void onNetworkStateChanged(final Intent intent) { - final boolean noConnection = intent.getBooleanExtra( - ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); - mIsNetworkConnected = !noConnection; - - KeyboardSwitcher.getInstance().onNetworkStateChanged(); - } - - ////////////////////////////////// - // Subtype Switching functions // - ////////////////////////////////// - public int getLanguageOnSpacebarFormatType(final RichInputMethodSubtype subtype) { return mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(subtype); } - - public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() { - final Locale systemLocale = mResources.getConfiguration().locale; - final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>(); - final InputMethodManager inputMethodManager = mRichImm.getInputMethodManager(); - final List<InputMethodInfo> enabledInputMethodInfoList = - inputMethodManager.getEnabledInputMethodList(); - for (final InputMethodInfo info : enabledInputMethodInfoList) { - final List<InputMethodSubtype> enabledSubtypes = - inputMethodManager.getEnabledInputMethodSubtypeList( - info, true /* allowsImplicitlySelectedSubtypes */); - if (enabledSubtypes.isEmpty()) { - // An IME with no subtypes is found. - return false; - } - enabledSubtypesOfEnabledImes.addAll(enabledSubtypes); - } - for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) { - if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty() - && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) { - return false; - } - } - return true; - } - - private static RichInputMethodSubtype sForcedSubtypeForTesting = null; - - @UsedForTesting - static void forceSubtype(final InputMethodSubtype subtype) { - sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype); - } - - public Locale[] getCurrentSubtypeLocales() { - if (null != sForcedSubtypeForTesting) { - return sForcedSubtypeForTesting.getLocales(); - } - return getCurrentSubtype().getLocales(); - } - - public RichInputMethodSubtype getCurrentSubtype() { - if (null != sForcedSubtypeForTesting) { - return sForcedSubtypeForTesting; - } - return mCurrentRichInputMethodSubtype; - } - - public RichInputMethodSubtype getNoLanguageSubtype() { - if (mNoLanguageSubtype == null) { - mNoLanguageSubtype = new RichInputMethodSubtype( - mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY)); - } - if (mNoLanguageSubtype != null) { - return mNoLanguageSubtype; - } - Log.w(TAG, "Can't find any language with QWERTY subtype"); - Log.w(TAG, "No input method subtype found; returning dummy subtype: " - + DUMMY_NO_LANGUAGE_SUBTYPE); - return DUMMY_NO_LANGUAGE_SUBTYPE; - } - - public RichInputMethodSubtype getEmojiSubtype() { - if (mEmojiSubtype == null) { - final InputMethodSubtype rawEmojiSubtype = - mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI); - if (null != rawEmojiSubtype) { - mEmojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype); - } - } - if (mEmojiSubtype != null) { - return mEmojiSubtype; - } - Log.w(TAG, "Can't find emoji subtype"); - Log.w(TAG, "No input method subtype found; returning dummy subtype: " - + DUMMY_EMOJI_SUBTYPE); - return DUMMY_EMOJI_SUBTYPE; - } - - public String getCombiningRulesExtraValueOfCurrentSubtype() { - return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype()); - } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 9b4619d35..ee8d3f8cb 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -230,7 +230,7 @@ public final class Suggest { inputStyle = inputStyleIfNotPrediction; } callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, - suggestionResults.mRawSuggestions, + suggestionResults.mRawSuggestions, typedWord, // TODO: this first argument is lying. If this is a whitelisted word which is an // actual word, it says typedWordValid = false, which looks wrong. We should either // rename the attribute or change the value. @@ -286,8 +286,12 @@ public final class Suggest { // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). // Note that because this method is never used to get predictions, there is no need to // modify inputType such in getSuggestedWordsForNonBatchInput. + final String pseudoTypedWord = suggestionsContainer.isEmpty() ? null + : suggestionsContainer.get(0).mWord; + callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, suggestionResults.mRawSuggestions, + pseudoTypedWord, true /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index c51e20f21..bddeac495 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -73,21 +73,11 @@ public class SuggestedWords { final boolean willAutoCorrect, final boolean isObsoleteSuggestions, final int inputStyle) { - this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect, - isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER); - } - - public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, - final ArrayList<SuggestedWordInfo> rawSuggestions, - final boolean typedWordValid, - final boolean willAutoCorrect, - final boolean isObsoleteSuggestions, - final int inputStyle, - final int sequenceNumber) { this(suggestedWordInfoList, rawSuggestions, (suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, - typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, sequenceNumber); + typedWordValid, willAutoCorrect, + isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER); } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index fa55319d2..78860d87d 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -21,10 +21,10 @@ import com.android.inputmethod.event.Event; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.ComposedData; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.common.InputPointers; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.CoordinateUtils; import java.util.ArrayList; import java.util.Collections; diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java index d4be0e397..78bfd2b52 100644 --- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java +++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java @@ -26,10 +26,10 @@ import android.os.Environment; import com.android.inputmethod.latin.BinaryDictionaryFileDumper; import com.android.inputmethod.latin.BinaryDictionaryGetter; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index 331f85e0e..ac2fc07c2 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -19,7 +19,7 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; import android.util.Log; -import com.android.inputmethod.latin.utils.FileUtils; +import com.android.inputmethod.latin.common.FileUtils; import java.io.File; import java.io.FilenameFilter; diff --git a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java index 49db2bdc0..c0ceb8857 100644 --- a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java @@ -24,7 +24,7 @@ import android.preference.Preference; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SubtypeSwitcher; +import com.android.inputmethod.latin.RichInputMethodManager; /** * "Preferences" settings sub screen. @@ -49,7 +49,7 @@ public final class PreferencesSettingsFragment extends SubScreenFragment { // When we are called from the Settings application but we are not already running, some // singleton and utility classes may not have been initialized. We have to call // initialization method of these classes here. See {@link LatinIME#onCreate()}. - SubtypeSwitcher.init(context); + RichInputMethodManager.init(context); final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); @@ -71,7 +71,7 @@ public final class PreferencesSettingsFragment extends SubScreenFragment { super.onResume(); final Preference voiceInputKeyOption = findPreference(Settings.PREF_VOICE_INPUT_KEY); if (voiceInputKeyOption != null) { - final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance() + final boolean isShortcutImeEnabled = RichInputMethodManager.getInstance() .isShortcutImeEnabled(); voiceInputKeyOption.setEnabled(isShortcutImeEnabled); voiceInputKeyOption.setSummary( diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 509b41fd3..26415e7d4 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -28,7 +28,6 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; -import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; @@ -140,7 +139,7 @@ public class SettingsValues { DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) && mInputAttributes.mShouldShowVoiceInputKey - && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); + && RichInputMethodManager.getInstance().isShortcutImeEnabled(); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 3ad8fb910..c90e8a3cf 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -34,10 +34,10 @@ import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -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.SuggestionResults; diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java index eda81940f..22fc35a42 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java @@ -28,7 +28,7 @@ import android.widget.EditText; import com.android.inputmethod.compat.UserDictionaryCompatUtils; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import java.util.ArrayList; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java index 90e4faafd..b9ed35375 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java @@ -31,7 +31,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import java.util.List; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java index e58727ec4..c0a946e42 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.userdictionary; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import android.content.Context; import android.text.TextUtils; diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index 24025b272..81c3e3c61 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -28,6 +28,7 @@ import com.android.inputmethod.latin.AssetFileAddress; import com.android.inputmethod.latin.BinaryDictionaryGetter; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java deleted file mode 100644 index c519a0de6..000000000 --- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2011 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 android.text.TextUtils; - -import java.util.HashMap; -import java.util.Locale; - -/** - * A class to help with handling Locales in string form. - * - * This file has the same meaning and features (and shares all of its code) with - * the one in the dictionary pack. They need to be kept synchronized; for any - * update/bugfix to this file, consider also updating/fixing the version in the - * dictionary pack. - */ -public final class LocaleUtils { - private LocaleUtils() { - // Intentional empty constructor for utility class. - } - - // Locale match level constants. - // A higher level of match is guaranteed to have a higher numerical value. - // Some room is left within constants to add match cases that may arise necessary - // in the future, for example differentiating between the case where the countries - // are both present and different, and the case where one of the locales does not - // specify the countries. This difference is not needed now. - - // Nothing matches. - public static final int LOCALE_NO_MATCH = 0; - // The languages matches, but the country are different. Or, the reference locale requires a - // country and the tested locale does not have one. - public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3; - // The languages and country match, but the variants are different. Or, the reference locale - // requires a variant and the tested locale does not have one. - public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6; - // The required locale is null or empty so it will accept anything, and the tested locale - // is non-null and non-empty. - public static final int LOCALE_ANY_MATCH = 10; - // The language matches, and the tested locale specifies a country but the reference locale - // does not require one. - public static final int LOCALE_LANGUAGE_MATCH = 15; - // The language and the country match, and the tested locale specifies a variant but the - // reference locale does not require one. - public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20; - // The compared locales are fully identical. This is the best match level. - public static final int LOCALE_FULL_MATCH = 30; - - // The level at which a match is "normally" considered a locale match with standard algorithms. - // Don't use this directly, use #isMatch to test. - private static final int LOCALE_MATCH = LOCALE_ANY_MATCH; - - // Make this match the maximum match level. If this evolves to have more than 2 digits - // when written in base 10, also adjust the getMatchLevelSortedString method. - private static final int MATCH_LEVEL_MAX = 30; - - /** - * Return how well a tested locale matches a reference locale. - * - * This will check the tested locale against the reference locale and return a measure of how - * a well it matches the reference. The general idea is that the tested locale has to match - * every specified part of the required locale. A full match occur when they are equal, a - * partial match when the tested locale agrees with the reference locale but is more specific, - * and a difference when the tested locale does not comply with all requirements from the - * reference locale. - * In more detail, if the reference locale specifies at least a language and the testedLocale - * does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the - * reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH - * if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and - * tested locale agree on the language, but not on the country, - * LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country, - * and LOCALE_LANGUAGE_MATCH otherwise. - * If they agree on both the language and the country, but not on the variant, - * LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale - * specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches, - * LOCALE_FULL_MATCH is returned. - * Examples: - * en <=> en_US => LOCALE_LANGUAGE_MATCH - * en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER - * en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER - * en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH - * sp_US <=> en_US => LOCALE_NO_MATCH - * de <=> de => LOCALE_FULL_MATCH - * en_US <=> en_US => LOCALE_FULL_MATCH - * "" <=> en_US => LOCALE_ANY_MATCH - * - * @param referenceLocale the reference locale to test against. - * @param testedLocale the locale to test. - * @return a constant that measures how well the tested locale matches the reference locale. - */ - public static int getMatchLevel(String referenceLocale, String testedLocale) { - if (TextUtils.isEmpty(referenceLocale)) { - return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH; - } - if (null == testedLocale) return LOCALE_NO_MATCH; - String[] referenceParams = referenceLocale.split("_", 3); - String[] testedParams = testedLocale.split("_", 3); - // By spec of String#split, [0] cannot be null and length cannot be 0. - if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH; - switch (referenceParams.length) { - case 1: - return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH; - case 2: - if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; - if (!referenceParams[1].equals(testedParams[1])) - return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; - if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH; - return LOCALE_FULL_MATCH; - case 3: - if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; - if (!referenceParams[1].equals(testedParams[1])) - return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; - if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER; - if (!referenceParams[2].equals(testedParams[2])) - return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER; - return LOCALE_FULL_MATCH; - } - // It should be impossible to come here - return LOCALE_NO_MATCH; - } - - /** - * Return a string that represents this match level, with better matches first. - * - * The strings are sorted in lexicographic order: a better match will always be less than - * a worse match when compared together. - */ - public static String getMatchLevelSortedString(int matchLevel) { - // This works because the match levels are 0~99 (actually 0~30) - // Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel - return String.format(Locale.ROOT, "%02d", MATCH_LEVEL_MAX - matchLevel); - } - - /** - * Find out whether a match level should be considered a match. - * - * This method takes a match level as returned by the #getMatchLevel method, and returns whether - * it should be considered a match in the usual sense with standard Locale functions. - * - * @param level the match level, as returned by getMatchLevel. - * @return whether this is a match or not. - */ - public static boolean isMatch(int level) { - return LOCALE_MATCH <= level; - } - - private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); - - /** - * Creates a locale from a string specification. - */ - public static Locale constructLocaleFromString(final String localeStr) { - if (localeStr == null) { - return null; - } - synchronized (sLocaleCache) { - Locale retval = sLocaleCache.get(localeStr); - if (retval != null) { - return retval; - } - String[] localeParams = localeStr.split("_", 3); - if (localeParams.length == 1) { - retval = new Locale(localeParams[0]); - } else if (localeParams.length == 2) { - retval = new Locale(localeParams[0], localeParams[1]); - } else if (localeParams.length == 3) { - retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); - } - if (retval != null) { - sLocaleCache.put(localeStr, retval); - } - return retval; - } - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 55c1dc9e5..b36168b6c 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -28,12 +28,15 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.common.StringUtils; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; +import javax.annotation.Nonnull; + /** * A helper class to deal with subtype locales. */ @@ -173,7 +176,8 @@ public final class SubtypeLocaleUtils { return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId; } - public static Locale getDisplayLocaleOfSubtypeLocale(final String localeString) { + @Nonnull + public static Locale getDisplayLocaleOfSubtypeLocale(@Nonnull final String localeString) { if (NO_LANGUAGE.equals(localeString)) { return sResources.getConfiguration().locale; } @@ -183,17 +187,20 @@ public final class SubtypeLocaleUtils { return LocaleUtils.constructLocaleFromString(localeString); } - public static String getSubtypeLocaleDisplayNameInSystemLocale(final String localeString) { + public static String getSubtypeLocaleDisplayNameInSystemLocale( + @Nonnull final String localeString) { final Locale displayLocale = sResources.getConfiguration().locale; return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale); } - public static String getSubtypeLocaleDisplayName(final String localeString) { + @Nonnull + public static String getSubtypeLocaleDisplayName(@Nonnull final String localeString) { final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale); } - public static String getSubtypeLanguageDisplayName(final String localeString) { + @Nonnull + public static String getSubtypeLanguageDisplayName(@Nonnull final String localeString) { final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString); final String languageString; if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) { @@ -205,8 +212,9 @@ public final class SubtypeLocaleUtils { return getSubtypeLocaleDisplayNameInternal(languageString, displayLocale); } - private static String getSubtypeLocaleDisplayNameInternal(final String localeString, - final Locale displayLocale) { + @Nonnull + private static String getSubtypeLocaleDisplayNameInternal(@Nonnull final String localeString, + @Nonnull final Locale displayLocale) { if (NO_LANGUAGE.equals(localeString)) { // No language subtype should be displayed in system locale. return sResources.getString(R.string.subtype_no_language); @@ -255,8 +263,9 @@ public final class SubtypeLocaleUtils { // en_US azerty T English (US) (AZERTY) exception // zz azerty T Alphabet (AZERTY) in system locale - private static String getReplacementString(final InputMethodSubtype subtype, - final Locale displayLocale) { + @Nonnull + private static String getReplacementString(@Nonnull final InputMethodSubtype subtype, + @Nonnull final Locale displayLocale) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) { return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME); @@ -264,20 +273,24 @@ public final class SubtypeLocaleUtils { return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale); } - public static String getSubtypeDisplayNameInSystemLocale(final InputMethodSubtype subtype) { + @Nonnull + public static String getSubtypeDisplayNameInSystemLocale( + @Nonnull final InputMethodSubtype subtype) { final Locale displayLocale = sResources.getConfiguration().locale; return getSubtypeDisplayNameInternal(subtype, displayLocale); } - public static String getSubtypeNameForLogging(final InputMethodSubtype subtype) { + @Nonnull + public static String getSubtypeNameForLogging(@Nonnull final InputMethodSubtype subtype) { if (subtype == null) { return "<null subtype>"; } return getSubtypeLocale(subtype) + "/" + getKeyboardLayoutSetName(subtype); } - private static String getSubtypeDisplayNameInternal(final InputMethodSubtype subtype, - final Locale displayLocale) { + @Nonnull + private static String getSubtypeDisplayNameInternal(@Nonnull final InputMethodSubtype subtype, + @Nonnull final Locale displayLocale) { final String replacementString = getReplacementString(subtype, displayLocale); // TODO: rework this for multi-lingual subtypes final int nameResId = subtype.getNameResId(); @@ -302,21 +315,26 @@ public final class SubtypeLocaleUtils { getSubtypeName.runInLocale(sResources, displayLocale), displayLocale); } - public static Locale getSubtypeLocale(final InputMethodSubtype subtype) { + @Nonnull + public static Locale getSubtypeLocale(@Nonnull final InputMethodSubtype subtype) { final String localeString = subtype.getLocale(); return LocaleUtils.constructLocaleFromString(localeString); } - public static String getKeyboardLayoutSetDisplayName(final InputMethodSubtype subtype) { + @Nonnull + public static String getKeyboardLayoutSetDisplayName( + @Nonnull final InputMethodSubtype subtype) { final String layoutName = getKeyboardLayoutSetName(subtype); return getKeyboardLayoutSetDisplayName(layoutName); } - public static String getKeyboardLayoutSetDisplayName(final String layoutName) { + @Nonnull + public static String getKeyboardLayoutSetDisplayName(@Nonnull final String layoutName) { return sKeyboardLayoutToDisplayNameMap.get(layoutName); } - public static String getKeyboardLayoutSetName(final RichInputMethodSubtype subtype) { + @Nonnull + public static String getKeyboardLayoutSetName(@Nonnull final RichInputMethodSubtype subtype) { return getKeyboardLayoutSetName(subtype.getRawSubtype()); } diff --git a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java index 94caf51ed..1ea68e471 100644 --- a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java @@ -30,7 +30,7 @@ import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyVisual; import com.android.inputmethod.latin.common.Constants; -import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java index 62625890e..2d38c874d 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java @@ -46,6 +46,11 @@ public final class TestsBengaliBD extends LayoutTestsBase { } @Override + public ExpectedKey[] getSpaceKeys(final boolean isPhone) { + return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY)); + } + + @Override public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; } // U+09F3: "৳" BENGALI RUPEE SIGN diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index 039330c87..cff489dd5 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -22,6 +22,8 @@ import android.util.Pair; import com.android.inputmethod.latin.NgramContext.WordInfo; import com.android.inputmethod.latin.common.CodePointUtils; +import com.android.inputmethod.latin.common.FileUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictionaryHeader; @@ -30,8 +32,6 @@ import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.FileUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index fcaa8cdca..60d2de18c 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -24,12 +24,12 @@ import android.util.Pair; import com.android.inputmethod.latin.NgramContext.WordInfo; import com.android.inputmethod.latin.common.CodePointUtils; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.FileUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.WeightedString; import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.io.IOException; diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 926a2d3e1..00e0b52cb 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -44,10 +44,10 @@ import com.android.inputmethod.latin.Dictionary.PhonyDictionary; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Locale; @@ -387,7 +387,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, 0 /* id */); - SubtypeSwitcher.forceSubtype(subtype); + RichInputMethodManager.forceSubtype(subtype); mLatinIME.onCurrentInputMethodSubtypeChanged(subtype); runMessages(); mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java index 20256e670..8ae475f7e 100644 --- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -20,8 +20,8 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; import com.android.inputmethod.latin.common.StringUtils; -import com.android.inputmethod.latin.utils.CoordinateUtils; /** * Unit tests for WordComposer. diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index d239f8dac..6c60fdc0c 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -26,6 +26,7 @@ import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.common.CodePointUtils; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; @@ -67,6 +68,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private static final SparseArray<List<Integer>> sChainBigrams = new SparseArray<>(); private static final HashMap<String, List<String>> sShortcuts = new HashMap<>(); + final Random mRandom; + public BinaryDictDecoderEncoderTests() { this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); } @@ -75,10 +78,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { super(); BinaryDictionaryUtils.setCurrentTimeForTest(0); Log.e(TAG, "Testing dictionary: seed is " + seed); - final Random random = new Random(seed); + mRandom = new Random(seed); sWords.clear(); sWordsWithVariousCodePoints.clear(); - generateWords(maxUnigrams, random); + generateWords(maxUnigrams, mRandom); for (int i = 0; i < sWords.size(); ++i) { sChainBigrams.put(i, new ArrayList<Integer>()); @@ -96,10 +99,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { sShortcuts.clear(); for (int i = 0; i < NUM_OF_NODES_HAVING_SHORTCUTS; ++i) { - final int from = Math.abs(random.nextInt()) % sWords.size(); + final int from = Math.abs(mRandom.nextInt()) % sWords.size(); sShortcuts.put(sWords.get(from), new ArrayList<String>()); for (int j = 0; j < NUM_OF_SHORTCUTS; ++j) { - final int to = Math.abs(random.nextInt()) % sWords.size(); + final int to = Math.abs(mRandom.nextInt()) % sWords.size(); sShortcuts.get(sWords.get(from)).add(sWords.get(to)); } } @@ -604,11 +607,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { + " : " + outputOptions(bufferType, formatOptions)); // Test a word that isn't contained within the dictionary. - final Random random = new Random((int)System.currentTimeMillis()); final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, - random); + mRandom); for (int i = 0; i < 1000; ++i) { - final String word = CodePointUtils.generateWord(random, codePointSet); + final String word = CodePointUtils.generateWord(mRandom, codePointSet); if (sWords.indexOf(word) != -1) continue; checkGetTerminalPosition(dictDecoder, word, false); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 120b96bc6..be75565bb 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -17,11 +17,16 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.LinkedList; + +import javax.annotation.Nonnull; /** * Decodes binary files for a FusionDictionary. @@ -361,6 +366,43 @@ public final class BinaryDictDecoderUtils { } /** + * Helper method that brutally decodes a header from a byte array. + * + * @param headerBuffer a buffer containing the bytes of the header. + * @return a hashmap of the attributes stored in the header + */ + @Nonnull + public static HashMap<String, String> decodeHeaderAttributes(@Nonnull final byte[] headerBuffer) + throws UnsupportedFormatException { + final StringBuilder sb = new StringBuilder(); + final LinkedList<String> keyValues = new LinkedList<>(); + int index = 0; + while (index < headerBuffer.length) { + if (headerBuffer[index] == FormatSpec.PTNODE_CHARACTERS_TERMINATOR) { + keyValues.add(sb.toString()); + sb.setLength(0); + } else if (CharEncoding.fitsOnOneByte(headerBuffer[index] & 0xFF, + null /* codePointTable */)) { + sb.appendCodePoint(headerBuffer[index] & 0xFF); + } else { + sb.appendCodePoint(((headerBuffer[index] & 0xFF) << 16) + + ((headerBuffer[index + 1] & 0xFF) << 8) + + (headerBuffer[index + 2] & 0xFF)); + index += 2; + } + index += 1; + } + if ((keyValues.size() & 1) != 0) { + throw new UnsupportedFormatException("Odd number of attributes"); + } + final HashMap<String, String> attributes = new HashMap<>(); + for (int i = 0; i < keyValues.size(); i += 2) { + attributes.put(keyValues.get(i), keyValues.get(i + 1)); + } + return attributes; + } + + /** * Helper method to pass a file name instead of a File object to isBinaryDictionary. */ public static boolean isBinaryDictionary(final String filename) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 7e54ce986..63ea89c1d 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.utils.FileUtils; +import com.android.inputmethod.latin.common.FileUtils; import java.io.File; import java.io.FileNotFoundException; diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index 155421922..1e4bd768c 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -20,10 +20,10 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.NgramContext; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; import java.io.File; import java.io.IOException; diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index 778f6e800..813a71239 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -23,9 +23,9 @@ import android.util.Log; import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.NgramContext; import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.FileUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.DistracterFilter; -import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.io.FilenameFilter; diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java index 131865ab2..f50b8e0b8 100644 --- a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java @@ -20,6 +20,8 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.common.FileUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java index 4646a823d..9680d85b3 100644 --- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java @@ -21,8 +21,8 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; -import com.android.inputmethod.latin.utils.LocaleUtils; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java index dc4e2e4bb..47fd5feaa 100644 --- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java @@ -19,6 +19,8 @@ package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import com.android.inputmethod.latin.common.CollectionUtils; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index 42659253a..1a9f029ae 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -47,14 +47,10 @@ LATINIME_SRC_FILES_FOR_DICTTOOL := \ latin/Dictionary.java \ latin/NgramContext.java \ latin/SuggestedWords.java \ - latin/settings/NativeSuggestOptions.java \ latin/settings/SettingsValuesForSuggestion.java \ latin/utils/BinaryDictionaryUtils.java \ latin/utils/CombinedFormatUtils.java \ - latin/utils/CoordinateUtils.java \ - latin/utils/FileUtils.java \ - latin/utils/JniUtils.java \ - latin/utils/LocaleUtils.java + latin/utils/JniUtils.java LATINIME_OVERRIDABLE_SRC_FILES_FOR_DICTTOOL := \ latin/define/DebugFlags.java diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java index 1c5dfa9fb..3ec28f313 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java @@ -19,6 +19,10 @@ package com.android.inputmethod.latin.dicttool; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.DictDecoder; +import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; @@ -27,12 +31,18 @@ import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * Class grouping utilities for offline dictionary making. @@ -44,26 +54,27 @@ public final class BinaryDictOffdeviceUtils { // Prefix and suffix are arbitrary, the values do not really matter private final static String PREFIX = "dicttool"; private final static String SUFFIX = ".tmp"; - private final static int COPY_BUFFER_SIZE = 8192; - public static class DecoderChainSpec { + public static class DecoderChainSpec<T> { public final static int COMPRESSION = 1; public final static int ENCRYPTION = 2; - private final static int MAX_DECODE_DEPTH = 4; - final int[] mDecoderSpec; - File mFile; + private final static int[][] VALID_DECODER_CHAINS = { + { }, { COMPRESSION }, { ENCRYPTION, COMPRESSION } + }; + + private final int mDecoderSpecIndex; + public T mResult; public DecoderChainSpec() { - mDecoderSpec = new int[0]; - mFile = null; + mDecoderSpecIndex = 0; + mResult = null; } - public DecoderChainSpec(final DecoderChainSpec src, final int newStep) { - mDecoderSpec = Arrays.copyOf(src.mDecoderSpec, src.mDecoderSpec.length + 1); - mDecoderSpec[src.mDecoderSpec.length] = newStep; - mFile = src.mFile; + private DecoderChainSpec(final DecoderChainSpec<T> src) { + mDecoderSpecIndex = src.mDecoderSpecIndex + 1; + mResult = src.mResult; } private String getStepDescription(final int step) { @@ -79,110 +90,165 @@ public final class BinaryDictOffdeviceUtils { public String describeChain() { final StringBuilder s = new StringBuilder("raw"); - for (final int step : mDecoderSpec) { + for (final int step : VALID_DECODER_CHAINS[mDecoderSpecIndex]) { s.append(" > "); s.append(getStepDescription(step)); } return s.toString(); } - } - public static void copy(final InputStream input, final OutputStream output) throws IOException { - final byte[] buffer = new byte[COPY_BUFFER_SIZE]; - for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) { - output.write(buffer, 0, readBytes); + /** + * Returns the next sequential spec. If exhausted, return null. + */ + public DecoderChainSpec next() { + if (mDecoderSpecIndex + 1 >= VALID_DECODER_CHAINS.length) { + return null; + } + return new DecoderChainSpec(this); } - } - /** - * Returns a decrypted/uncompressed dictionary. - * - * This will decrypt/uncompress any number of times as necessary until it finds the - * dictionary signature, and copy the decoded file to a temporary place. - * If this is not a dictionary, the method returns null. - */ - public static DecoderChainSpec getRawDictionaryOrNull(final File src) { - return getRawDictionaryOrNullInternal(new DecoderChainSpec(), src, 0); + public InputStream getStream(final File src) throws FileNotFoundException, IOException { + InputStream input = new BufferedInputStream(new FileInputStream(src)); + for (final int step : VALID_DECODER_CHAINS[mDecoderSpecIndex]) { + switch (step) { + case COMPRESSION: + input = Compress.getUncompressedStream(input); + break; + case ENCRYPTION: + input = Crypt.getDecryptedStream(input); + break; + } + } + return input; + } } - private static DecoderChainSpec getRawDictionaryOrNullInternal( - final DecoderChainSpec spec, final File src, final int depth) { - // Unfortunately the decoding scheme we use can consider any data to be encrypted - // and will produce some output, meaning it's not possible to reliably detect encrypted - // data. Thus, some non-dictionary files (especially small) ones may successfully decrypt - // over and over, ending in a stack overflow. Hence we limit the depth at which we try - // decoding the file. - if (depth > DecoderChainSpec.MAX_DECODE_DEPTH) { - return null; - } - if (BinaryDictDecoderUtils.isBinaryDictionary(src) - || CombinedInputOutput.isCombinedDictionary(src.getAbsolutePath())) { - spec.mFile = src; - return spec; - } - // It's not a raw dictionary - try to see if it's compressed. - final File uncompressedFile = tryGetUncompressedFile(src); - if (null != uncompressedFile) { - final DecoderChainSpec newSpec = - getRawDictionaryOrNullInternal(spec, uncompressedFile, depth + 1); - if (null == newSpec) return null; - return new DecoderChainSpec(newSpec, DecoderChainSpec.COMPRESSION); - } - // It's not a compressed either - try to see if it's crypted. - final File decryptedFile = tryGetDecryptedFile(src); - if (null != decryptedFile) { - final DecoderChainSpec newSpec = - getRawDictionaryOrNullInternal(spec, decryptedFile, depth + 1); - if (null == newSpec) return null; - return new DecoderChainSpec(newSpec, DecoderChainSpec.ENCRYPTION); - } - return null; + public interface InputProcessor<T> { + @Nonnull + public T process(@Nonnull final InputStream input) + throws IOException, UnsupportedFormatException; } - /* Try to uncompress the file passed as an argument. - * - * If the file can be uncompressed, the uncompressed version is returned. Otherwise, null - * is returned. - */ - private static File tryGetUncompressedFile(final File src) { - try { + public static class CopyProcessor implements InputProcessor<File> { + @Override @Nonnull + public File process(@Nonnull final InputStream input) throws IOException, + UnsupportedFormatException { final File dst = File.createTempFile(PREFIX, SUFFIX); dst.deleteOnExit(); - try ( - final InputStream input = Compress.getUncompressedStream( - new BufferedInputStream(new FileInputStream(src))); - final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst)) - ) { + try (final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))) { copy(input, output); - return dst; + output.flush(); + output.close(); + if (BinaryDictDecoderUtils.isBinaryDictionary(dst) + || CombinedInputOutput.isCombinedDictionary(dst.getAbsolutePath())) { + return dst; + } } - } catch (final IOException e) { - // Could not uncompress the file: presumably the file is simply not a compressed file - return null; + throw new UnsupportedFormatException("Input stream not at the expected format"); } } - /* Try to decrypt the file passed as an argument. + public static class HeaderReaderProcessor implements InputProcessor<DictionaryHeader> { + // Arbitrarily limit the header length to 32k. Sounds like it would never be larger + // than this. Revisit this if needed later. + private final int MAX_HEADER_LENGTH = 32 * 1024; + @Override @Nonnull + public DictionaryHeader process(final InputStream input) throws IOException, + UnsupportedFormatException { + // Do everything as curtly and ad-hoc as possible for performance. + final byte[] tmpBuffer = new byte[12]; + if (tmpBuffer.length != input.read(tmpBuffer)) { + throw new UnsupportedFormatException("File too short, not a dictionary"); + } + // Ad-hoc check for the magic number. See FormatSpec.java as well as + // byte_array_utils.h and BinaryDictEncoderUtils#writeDictionaryHeader(). + final int MAGIC_NUMBER_START_OFFSET = 0; + final int VERSION_START_OFFSET = 4; + final int HEADER_SIZE_OFFSET = 8; + final int magicNumber = ((tmpBuffer[MAGIC_NUMBER_START_OFFSET] & 0xFF) << 24) + + ((tmpBuffer[MAGIC_NUMBER_START_OFFSET + 1] & 0xFF) << 16) + + ((tmpBuffer[MAGIC_NUMBER_START_OFFSET + 2] & 0xFF) << 8) + + (tmpBuffer[MAGIC_NUMBER_START_OFFSET + 3] & 0xFF); + if (magicNumber != FormatSpec.MAGIC_NUMBER) { + throw new UnsupportedFormatException("Wrong magic number"); + } + final int version = ((tmpBuffer[VERSION_START_OFFSET] & 0xFF) << 8) + + (tmpBuffer[VERSION_START_OFFSET + 1] & 0xFF); + if (version != FormatSpec.VERSION2 && version != FormatSpec.VERSION201 + && version != FormatSpec.VERSION202) { + throw new UnsupportedFormatException("Only versions 2, 201, 202 are supported"); + } + final int totalHeaderSize = ((tmpBuffer[HEADER_SIZE_OFFSET] & 0xFF) << 24) + + ((tmpBuffer[HEADER_SIZE_OFFSET + 1] & 0xFF) << 16) + + ((tmpBuffer[HEADER_SIZE_OFFSET + 2] & 0xFF) << 8) + + (tmpBuffer[HEADER_SIZE_OFFSET + 3] & 0xFF); + if (totalHeaderSize > MAX_HEADER_LENGTH) { + throw new UnsupportedFormatException("Header too large"); + } + final byte[] headerBuffer = new byte[totalHeaderSize - tmpBuffer.length]; + if (headerBuffer.length != input.read(headerBuffer)) { + throw new UnsupportedFormatException("File shorter than specified in the header"); + } + final HashMap<String, String> attributes = + BinaryDictDecoderUtils.decodeHeaderAttributes(headerBuffer); + return new DictionaryHeader(totalHeaderSize, new DictionaryOptions(attributes), + new FormatOptions(version, false /* hasTimestamp */)); + } + } + + public static void copy(final InputStream input, final OutputStream output) throws IOException { + final byte[] buffer = new byte[COPY_BUFFER_SIZE]; + for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) { + output.write(buffer, 0, readBytes); + } + } + + /** + * Process a dictionary, decrypting/uncompressing it on the fly as necessary. * - * If the file can be decrypted, the decrypted version is returned. Otherwise, null - * is returned. + * This will execute the given processor repeatedly with the possible alternatives + * for dictionary format until the processor does not throw an exception. + * If the processor succeeds for none of the possible formats, the method returns null. */ - private static File tryGetDecryptedFile(final File src) { - try { - final File dst = File.createTempFile(PREFIX, SUFFIX); - dst.deleteOnExit(); - try ( - final InputStream input = Crypt.getDecryptedStream( - new BufferedInputStream(new FileInputStream(src))); - final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst)) - ) { - copy(input, output); - return dst; + @Nullable + public static <T> DecoderChainSpec<T> decodeDictionaryForProcess(@Nonnull final File src, + @Nonnull final InputProcessor<T> processor) { + @Nonnull DecoderChainSpec spec = new DecoderChainSpec(); + while (null != spec) { + try { + final InputStream input = spec.getStream(src); + spec.mResult = processor.process(input); + try { + input.close(); + } catch (IOException e) { + // CipherInputStream doesn't like being closed without having read the + // entire stream, for some reason. But we don't want to because it's a waste + // of resources. We really, really don't care about this. + // However on close() CipherInputStream does throw this exception, wrapped + // in an IOException so we need to catch it. + if (!(e.getCause() instanceof javax.crypto.BadPaddingException)) { + throw e; + } + } + return spec; + } catch (IOException | UnsupportedFormatException | ArrayIndexOutOfBoundsException e) { + // If the format is not the right one for this file, the processor will throw one + // of these exceptions. In our case, that means we should try the next spec, + // since it may still be at another format we haven't tried yet. + // TODO: stop using exceptions for this non-exceptional case. } - } catch (final IOException e) { - // Could not decrypt the file: presumably the file is simply not a crypted file - return null; + spec = spec.next(); } + return null; + } + + /** + * Get a decoder chain spec with a raw dictionary file. This makes a new file on the + * disk ready for any treatment the client wants. + */ + @Nullable + public static DecoderChainSpec<File> getRawDictionaryOrNull(@Nonnull final File src) { + return decodeDictionaryForProcess(src, new CopyProcessor()); } static FusionDictionary getDictionary(final String filename, final boolean report) { @@ -192,28 +258,28 @@ public final class BinaryDictOffdeviceUtils { System.out.println("Size : " + file.length() + " bytes"); } try { - final DecoderChainSpec decodedSpec = getRawDictionaryOrNull(file); + final DecoderChainSpec<File> decodedSpec = getRawDictionaryOrNull(file); if (null == decodedSpec) { throw new RuntimeException("Does not seem to be a dictionary file " + filename); } - if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mFile.getAbsolutePath())) { + if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mResult.getAbsolutePath())) { if (report) { System.out.println("Format : Combined format"); System.out.println("Packaging : " + decodedSpec.describeChain()); - System.out.println("Uncompressed size : " + decodedSpec.mFile.length()); + System.out.println("Uncompressed size : " + decodedSpec.mResult.length()); } try (final BufferedReader reader = new BufferedReader( - new InputStreamReader(new FileInputStream(decodedSpec.mFile), "UTF-8"))) { + new InputStreamReader(new FileInputStream(decodedSpec.mResult), "UTF-8"))) { return CombinedInputOutput.readDictionaryCombined(reader); } } final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder( - decodedSpec.mFile, 0, decodedSpec.mFile.length(), + decodedSpec.mResult, 0, decodedSpec.mResult.length(), DictDecoder.USE_BYTEARRAY); if (report) { System.out.println("Format : Binary dictionary format"); System.out.println("Packaging : " + decodedSpec.describeChain()); - System.out.println("Uncompressed size : " + decodedSpec.mFile.length()); + System.out.println("Uncompressed size : " + decodedSpec.mResult.length()); } return dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */); } catch (final IOException | UnsupportedFormatException e) { diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java index 07450ca51..8fdf7633f 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java @@ -20,6 +20,7 @@ public class CommandList { public static void populate() { // TODO: Move some commands to native code. Dicttool.addCommand("info", Info.class); + Dicttool.addCommand("header", Header.class); Dicttool.addCommand("diff", Diff.class); Dicttool.addCommand("compress", Compress.Compressor.class); Dicttool.addCommand("uncompress", Compress.Uncompressor.class); diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java new file mode 100644 index 000000000..51efdec33 --- /dev/null +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin.dicttool; + +import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils.DecoderChainSpec; +import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + +import java.io.File; +import java.util.Arrays; +import java.util.Locale; + +public class Header extends Dicttool.Command { + public static final String COMMAND = "header"; + + public Header() { + } + + @Override + public String getHelp() { + return COMMAND + " <filename>: prints the header contents of a dictionary file"; + } + + @Override + public void run() throws UnsupportedFormatException { + final boolean plumbing; + if (mArgs.length > 0 && "-p".equals(mArgs[0])) { + plumbing = true; + mArgs = Arrays.copyOfRange(mArgs, 1, mArgs.length); + } else { + plumbing = false; + } + if (mArgs.length < 1) { + throw new RuntimeException("Not enough arguments for command " + COMMAND); + } + final String filename = mArgs[0]; + final File dictFile = new File(filename); + final DecoderChainSpec<DictionaryHeader> spec = + BinaryDictOffdeviceUtils.decodeDictionaryForProcess(dictFile, + new BinaryDictOffdeviceUtils.HeaderReaderProcessor()); + if (null == spec) { + throw new UnsupportedFormatException(filename + + " doesn't seem to be a valid version 2 dictionary file"); + } + + final DictionaryHeader header = spec.mResult; + System.out.println("Dictionary : " + dictFile.getAbsolutePath()); + System.out.println("Size : " + dictFile.length() + " bytes"); + System.out.println("Format : Binary dictionary format"); + System.out.println("Packaging : " + spec.describeChain()); + System.out.println("Header attributes :"); + System.out.print(header.mDictionaryOptions.toString(2 /* indentCount */, plumbing)); + } +} diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java index 47ea70629..4e5c0742e 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java @@ -77,16 +77,16 @@ public class Package { if (mArgs.length != 2) { throw new RuntimeException("Too many/too few arguments for command " + COMMAND); } - final BinaryDictOffdeviceUtils.DecoderChainSpec decodedSpec = + final BinaryDictOffdeviceUtils.DecoderChainSpec<File> decodedSpec = BinaryDictOffdeviceUtils.getRawDictionaryOrNull(new File(mArgs[0])); if (null == decodedSpec) { System.out.println(mArgs[0] + " does not seem to be a dictionary"); return; } System.out.println("Packaging : " + decodedSpec.describeChain()); - System.out.println("Uncompressed size : " + decodedSpec.mFile.length()); + System.out.println("Uncompressed size : " + decodedSpec.mResult.length()); try ( - final InputStream input = getFileInputStream(decodedSpec.mFile); + final InputStream input = getFileInputStream(decodedSpec.mResult); final OutputStream output = new BufferedOutputStream( getFileOutputStreamOrStdOut(mArgs[1])) ) { diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java index b6383d788..e2dd5199b 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java @@ -16,10 +16,10 @@ package com.android.inputmethod.latin.dicttool; +import com.android.inputmethod.latin.common.FileUtils; import com.android.inputmethod.latin.makedict.BinaryDictDecoderEncoderTests; import com.android.inputmethod.latin.makedict.BinaryDictEncoderFlattenTreeTests; import com.android.inputmethod.latin.makedict.FusionDictionaryTest; -import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.io.IOException; diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java index 6cdbff7e5..ea9d4cc19 100644 --- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java +++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java @@ -16,10 +16,17 @@ package com.android.inputmethod.latin.dicttool; +import com.android.inputmethod.latin.common.CodePointUtils; +import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils; +import com.android.inputmethod.latin.dicttool.Compress; +import com.android.inputmethod.latin.dicttool.Crypt; +import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils.DecoderChainSpec; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; +import com.android.inputmethod.latin.makedict.BinaryDictUtils; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictEncoder; import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary; @@ -35,13 +42,37 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; /** * Unit tests for BinaryDictOffdeviceUtils */ public class BinaryDictOffdeviceUtilsTests extends TestCase { private static final int TEST_FREQ = 37; // Some arbitrary value unlikely to happen by chance + private static final int CODE_POINT_SET_SIZE = 300; + final Random mRandom; + private static final ArrayList<String> sWords = new ArrayList<>(); + + public BinaryDictOffdeviceUtilsTests(final long seed, final int maxUnigrams) { + super(); + mRandom = new Random(seed); + sWords.clear(); + generateWords(maxUnigrams, mRandom); + } + + private static void generateWords(final int maxUnigrams, final Random random) { + final int[] codePointSet = CodePointUtils.generateCodePointSet( + CODE_POINT_SET_SIZE, random); + final Set<String> wordSet = new HashSet<>(); + while (wordSet.size() < maxUnigrams) { + wordSet.add(CodePointUtils.generateWord(random, codePointSet)); + } + sWords.addAll(wordSet); + } public void testGetRawDictWorks() throws IOException, UnsupportedFormatException { final String VERSION = "1"; @@ -68,23 +99,17 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase { final File dst = File.createTempFile("testGetRawDict", ".tmp"); dst.deleteOnExit(); try (final OutputStream out = Compress.getCompressedStream( - Compress.getCompressedStream( - Compress.getCompressedStream( - new BufferedOutputStream(new FileOutputStream(dst)))))) { + new BufferedOutputStream(new FileOutputStream(dst)))) { final DictEncoder dictEncoder = new Ver2DictEncoder(out); - dictEncoder.writeDictionary(dict, new FormatOptions(2, false)); + dictEncoder.writeDictionary(dict, new FormatOptions(FormatSpec.VERSION202, false)); } // Test for an actually compressed dictionary and its contents - final BinaryDictOffdeviceUtils.DecoderChainSpec decodeSpec = + final BinaryDictOffdeviceUtils.DecoderChainSpec<File> decodeSpec = BinaryDictOffdeviceUtils.getRawDictionaryOrNull(dst); - for (final int step : decodeSpec.mDecoderSpec) { - assertEquals("Wrong decode spec", - BinaryDictOffdeviceUtils.DecoderChainSpec.COMPRESSION, step); - } - assertEquals("Wrong decode spec", 3, decodeSpec.mDecoderSpec.length); - final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mFile, 0, - decodeSpec.mFile.length()); + assertEquals("Wrong decode spec", "raw > compression", decodeSpec.describeChain()); + final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mResult, 0, + decodeSpec.mResult.length()); final FusionDictionary resultDict = dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */); assertEquals("Wrong version attribute", VERSION, resultDict.mOptions.mAttributes.get( @@ -125,4 +150,64 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase { assertNull("Wrongly identified data file", BinaryDictOffdeviceUtils.getRawDictionaryOrNull(gzDst)); } + + public void runTestHeaderReaderProcessorWithOneSpec(final boolean compress, final boolean crypt) + throws IOException, UnsupportedFormatException { + final String dictName = "testHeaderReaderProcessor"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS; + final int MAX_NUMBER_OF_OPTIONS_TO_ADD = 5; + final HashMap<String, String> options = new HashMap<>(); + // Required attributes + options.put("dictionary", "main:en_US"); + options.put("locale", "en_US"); + options.put("version", Integer.toString(mRandom.nextInt())); + // Add some random options for test + final int numberOfOptionsToAdd = mRandom.nextInt() % (MAX_NUMBER_OF_OPTIONS_TO_ADD + 1); + for (int i = 0; i < numberOfOptionsToAdd; ++i) { + options.put(sWords.get(2 * i), sWords.get(2 * 1 + 1)); + } + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + new DictionaryOptions(options)); + + for (int i = 0; i < sWords.size(); ++i) { + final String word = sWords.get(i); + dict.add(word, new ProbabilityInfo(TEST_FREQ), null /* shortcuts */, + false /* isNotAWord */, false /* isPossiblyOffensive */); + } + + File file = File.createTempFile(dictName, ".tmp"); + final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions); + dictEncoder.writeDictionary(dict, formatOptions); + + if (compress) { + final File rawFile = file; + file = File.createTempFile(dictName + ".compress", ".tmp"); + final Compress.Compressor compressCommand = new Compress.Compressor(); + compressCommand.setArgs(new String[] { rawFile.getPath(), file.getPath() }); + compressCommand.run(); + } + if (crypt) { + final File rawFile = file; + file = File.createTempFile(dictName + ".crypt", ".tmp"); + final Crypt.Encrypter cryptCommand = new Crypt.Encrypter(); + cryptCommand.setArgs(new String[] { rawFile.getPath(), file.getPath() }); + cryptCommand.run(); + } + + final DecoderChainSpec<DictionaryHeader> spec = + BinaryDictOffdeviceUtils.decodeDictionaryForProcess(file, + new BinaryDictOffdeviceUtils.HeaderReaderProcessor()); + assertNotNull("Can't decode a dictionary we just wrote : " + file, spec); + final DictionaryHeader header = spec.mResult; + assertEquals("raw" + (crypt ? " > encryption" : "") + (compress ? " > compression" : ""), + spec.describeChain()); + assertEquals(header.mDictionaryOptions.mAttributes, options); + } + + public void testHeaderReaderProcessor() throws IOException, UnsupportedFormatException { + runTestHeaderReaderProcessorWithOneSpec(false /* compress */, false /* crypt */); + runTestHeaderReaderProcessorWithOneSpec(true /* compress */, false /* crypt */); + runTestHeaderReaderProcessorWithOneSpec(true /* compress */, true /* crypt */); + } } |