diff options
192 files changed, 2382 insertions, 1536 deletions
diff --git a/java-overridable/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java b/java-overridable/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java new file mode 100644 index 000000000..c0a599c6e --- /dev/null +++ b/java-overridable/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java @@ -0,0 +1,66 @@ +/* + * 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.accounts; + +import android.support.annotation.NonNull; + +import javax.annotation.Nullable; + +/** + * Handles changes to account used to sign in to the keyboard. + * e.g. account switching/sign-in/sign-out from the keyboard + * user toggling the sync preference. + */ +public class AccountStateChangedListener { + + /** + * Called when the current account being used in keyboard is signed out. + * + * @param oldAccount the account that was signed out of. + */ + public static void onAccountSignedOut(@NonNull String oldAccount) { + } + + /** + * Called when the user signs-in to the keyboard. + * This may be called when the user switches accounts to sign in with a different account. + * + * @param oldAccount the previous account that was being used for sign-in. + * May be null for a fresh sign-in. + * @param newAccount the account being used for sign-in. + */ + public static void onAccountSignedIn(@Nullable String oldAccount, @NonNull String newAccount) { + } + + /** + * Called when the user toggles the sync preference. + * + * @param account the account being used for sync. + * @param syncEnabled indicates whether sync has been enabled or not. + */ + public static void onSyncPreferenceChanged(@Nullable String account, boolean syncEnabled) { + } + + /** + * Forces an immediate sync to happen. + * This should only be used for debugging purposes. + * + * @param account the account to use for sync. + */ + public static void forceSync(@Nullable String account) { + } +} diff --git a/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java b/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java index 70f152acb..e07a9f358 100644 --- a/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java @@ -16,16 +16,20 @@ package com.android.inputmethod.latin.accounts; -import android.accounts.Account; import android.content.Context; import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * Utility class for retrieving accounts that may be used for login. */ public class LoginAccountUtils { + /** + * This defines the type of account this class deals with. + * This account type is used when listing the accounts available on the device for login. + */ + public static final String ACCOUNT_TYPE = ""; + private LoginAccountUtils() { // This utility class is not publicly instantiable. } @@ -39,9 +43,4 @@ public class LoginAccountUtils { public static String[] getAccountsForLogin(final Context context) { return new String[0]; } - - @Nullable - public static Account getCurrentAccount(final Context context) { - return null; - } } diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java index 10fc612e7..99b958952 100644 --- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java +++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java @@ -46,5 +46,5 @@ public final class ProductionFlags { /** * When {@code true}, personal dictionary sync feature is ready to be enabled. */ - public static final boolean ENABLE_PERSONAL_DICTIONARY_SYNC = false; + public static final boolean ENABLE_PERSONAL_DICTIONARY_SYNC = ENABLE_ACCOUNT_SIGN_IN && false; } 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 1c02d7d63..1dfaf259e 100644 --- a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java @@ -19,6 +19,12 @@ package com.android.inputmethod.latin.settings; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceFragment; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.RichInputMethodManager; + +import javax.annotation.Nonnull; /** * Utility class for managing additional features settings. @@ -39,4 +45,11 @@ public class AdditionalFeaturesSettingUtils { final SharedPreferences prefs, final int[] additionalFeaturesPreferences) { // do nothing. } + + public static RichInputMethodSubtype createRichInputMethodSubtype( + @Nonnull final RichInputMethodManager imm, + @Nonnull final InputMethodSubtype subtype, + final Context context) { + return new RichInputMethodSubtype(subtype); + } } diff --git a/java-overridable/src/com/android/inputmethod/latin/sync/BeanstalkManager.java b/java-overridable/src/com/android/inputmethod/latin/sync/BeanstalkManager.java deleted file mode 100644 index 2242d9244..000000000 --- a/java-overridable/src/com/android/inputmethod/latin/sync/BeanstalkManager.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.sync; - -import android.content.Context; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.GuardedBy; - -public class BeanstalkManager { - private static final Object sLock = new Object(); - - @GuardedBy("sLock") - private static BeanstalkManager sInstance; - - /** - * @return the singleton instance of {@link BeanstalkManager}. - */ - @Nonnull - public static BeanstalkManager getInstance(Context context) { - synchronized(sLock) { - if (sInstance == null) { - sInstance = new BeanstalkManager(context.getApplicationContext()); - } - } - return sInstance; - } - - private BeanstalkManager(final Context context) { - // Intentional private constructor for singleton. - } - - public void onCreate() { - } - - public void requestSync() { - } - - public void onDestroy() { - } -}
\ No newline at end of file diff --git a/java/res/drawable-hdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-hdpi/ic_add_circle_white_24dp.png Binary files differindex 314c52137..314c52137 100644 --- a/java/res/drawable-hdpi/ic_add_circle_wht_24dp.png +++ b/java/res/drawable-hdpi/ic_add_circle_white_24dp.png diff --git a/java/res/drawable-mdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-mdpi/ic_add_circle_white_24dp.png Binary files differindex 11363b173..11363b173 100644 --- a/java/res/drawable-mdpi/ic_add_circle_wht_24dp.png +++ b/java/res/drawable-mdpi/ic_add_circle_white_24dp.png diff --git a/java/res/drawable-xhdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-xhdpi/ic_add_circle_white_24dp.png Binary files differindex 32a5b05ba..32a5b05ba 100644 --- a/java/res/drawable-xhdpi/ic_add_circle_wht_24dp.png +++ b/java/res/drawable-xhdpi/ic_add_circle_white_24dp.png diff --git a/java/res/drawable-xxhdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-xxhdpi/ic_add_circle_white_24dp.png Binary files differindex a22c463f9..a22c463f9 100644 --- a/java/res/drawable-xxhdpi/ic_add_circle_wht_24dp.png +++ b/java/res/drawable-xxhdpi/ic_add_circle_white_24dp.png diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml index 46551f63f..ae3c19db5 100644 --- a/java/res/layout/input_view.xml +++ b/java/res/layout/input_view.xml @@ -21,7 +21,8 @@ <com.android.inputmethod.latin.InputView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + style="?attr/inputViewStyle"> <include android:id="@+id/main_keyboard_frame" layout="@layout/main_keyboard_frame" /> diff --git a/java/res/menu/add_style.xml b/java/res/menu/add_style.xml index d1cab4bb5..befa3f2aa 100644 --- a/java/res/menu/add_style.xml +++ b/java/res/menu/add_style.xml @@ -20,7 +20,7 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_add_style" - android:icon="@drawable/ic_add_circle_wht_24dp" + android:icon="@drawable/ic_add_circle_white_24dp" android:title="@string/add_style" android:showAsAction="always" /> -</menu>
\ No newline at end of file +</menu> diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 5f3a0676c..cbdf434fa 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Dieselfde invoerstyl bestaan reeds: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Sleuteldruk se vibrasie-tydsduur"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Sleuteldruk se klankvolume"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Vertraging van sleutellangdruk"</string> <string name="button_default" msgid="3988017840431881491">"Verstek"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom by <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Gebaar-tik"</string> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 4e9c4ca7a..1ac1f8cbf 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ተመሳሳዩ የግብዓት ቅጥ አስቀድሞ አለ፦ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"የቁልፍ ጭነት ንዝረት ርዝመት"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"የቁልፍ ጭነት ድምጽ መጠን"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"የሰሌዳ ቁልፍ ጠቅታ በመጫን መዘግየት"</string> <string name="button_default" msgid="3988017840431881491">"ነባሪ"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"እንኳን ወደ <xliff:g id="APPLICATION_NAME">%s</xliff:g> በደህና መጡ"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"በጣት ምልክት መተየብ"</string> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index b5e9c5006..dbab0efe8 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"نمط الإدخال ذاته موجود من قبل: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"مدة اهتزاز الضغط على المفاتيح"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"مستوى صوت الضغط على المفاتيح"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"تأخير الضغط الطويل للمفاتيح"</string> <string name="button_default" msgid="3988017840431881491">"الافتراضية"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"مرحبا بكم في <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"مع الكتابة بالإشارة"</string> diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml index 238ed0f53..463faf505 100644 --- a/java/res/values-az-rAZ/strings.xml +++ b/java/res/values-az-rAZ/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Eyni daxiletmə üslubu artıq mövcuddur: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasiyalı klikləmə müddəti"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Səsli klikləmə səsi"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavişi uzun müddət basmada gecikmə"</string> <string name="button_default" msgid="3988017840431881491">"Defolt"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> təbiqinə xoş gəlmisiniz"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Jest Yazısı ilə"</string> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index ed40074b1..21df08466 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Същият стил на въвеждане вече съществува: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Продълж. на вибриране при натискане"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Сила на звука при натиск. на клавиш"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Забавяне при продълж. натискане"</string> <string name="button_default" msgid="3988017840431881491">"Стандартни"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Добре дошли в/ъв <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"с въвеждане чрез жест"</string> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index e39247f21..da93a58cf 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ja existeix aquest estil d\'entrada: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durada vibració en prémer"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volum del so en prémer tecles"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retard en mantenir premut"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminat"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Et donem la benvinguda a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"amb Escriptura gestual"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index f52755712..996bc7012 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Tento styl zadávání již existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Délka vibrace u stisku klávesy"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Hlasitost stisknutí klávesy"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Prodleva dlouhého stisknutí"</string> <string name="button_default" msgid="3988017840431881491">"Výchozí"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Vítá vás <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s psaním gesty"</string> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 240250f56..fe1820b96 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Denne inputstil findes allerede: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationstid ved tastetryk"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Lydstyrke ved tastetryk"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Forsinket langt tastetryk"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med glidende indtastning"</string> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index b59c5069c..13c012565 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Der gleiche Eingabestil ist bereits vorhanden: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationsdauer bei Tastendruck"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tonlautstärke bei Tastendruck"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Verzögerung für langes Drücken"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Willkommen bei <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"mit Bewegungseingabe"</string> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index e519e4379..f4f58e115 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Το ίδιο στυλ εισόδου υπάρχει ήδη: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Διάρκεια δόνησης πατήμ. πλήκτ."</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ένταση ήχου πατήματος πλήκτρου"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Καθυστέρηση παρατεταμένου πατήματος πλήκτρου"</string> <string name="button_default" msgid="3988017840431881491">"Προεπιλογή"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Καλώς ορίσατε στο <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"με Πληκτρολόγηση με κίνηση"</string> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index f31b0713c..249f8f30f 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welcome to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"with Gesture Typing"</string> diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml index f31b0713c..249f8f30f 100644 --- a/java/res/values-en-rIN/strings.xml +++ b/java/res/values-en-rIN/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welcome to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"with Gesture Typing"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 79b0f922d..f0aebbeb8 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durac. vibrac. al presionar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Vol. sonido al presionar tecla"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Demora de presión prolongada"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Te damos la bienvenida a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura gestual"</string> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index d099e4f4b..a470bc9c5 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duración de vibración al pulsar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volumen sonido al pulsar tecla"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retraso de pulsación prolongada"</string> <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Te damos la bienvenida a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con escritura gestual"</string> diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml index e7939e20a..ffbaba047 100644 --- a/java/res/values-et-rEE/strings.xml +++ b/java/res/values-et-rEE/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sama sisendstiil on juba olemas: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Klahvivajutuse vibreerimise kestus"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Klahvivajutuse helitugevus"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Pika klahvivajutuse viide"</string> <string name="button_default" msgid="3988017840431881491">"Vaikeväärtus"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Tere tulemast rakendusse <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"joonistusega sisestamisega"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 336c8ed15..31627844c 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"سبک ورودی مشابهی در حال حاضر وجود دارد: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"طول مدت لرزش در اثر فشردن کلید"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"میزان صدای فشردن کلید"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"تأخیر فشار طولانی کلید"</string> <string name="button_default" msgid="3988017840431881491">"پیشفرض"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"به <xliff:g id="APPLICATION_NAME">%s</xliff:g> خوش آمدید"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"با ورودی اشارهای"</string> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index d1bb592e4..3739cdf79 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Syöttötyyli on jo olemassa: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Painalluksen värinän kesto"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Näppäinpainalluksen äänenvoim."</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Pitkän painalluksen viive"</string> <string name="button_default" msgid="3988017840431881491">"Oletusarvot"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Tervetuloa käyttämään sovellusta <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ja piirtokirjoitus"</string> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index 98796524b..e5492a1a4 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume pression de touche"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string> <string name="button_default" msgid="3988017840431881491">"Par défaut"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index a93c0efe1..df6167cd1 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume son pression de touche"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string> <string name="button_default" msgid="3988017840431881491">"Par défaut"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 5f1ca5c65..3cf27b58c 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ऐसी ही इनपुट शैली पहले से मौजूद है: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुंजी-स्पर्श कंपन अवधि"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुंजी-स्पर्श ध्वनि आवाज़"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुंजी को देर तक दबाने का विलंब"</string> <string name="button_default" msgid="3988017840431881491">"सामान्य"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> में आपका स्वागत है"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"हावभाव लेखन के साथ"</string> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index 34449c289..b13a99734 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Već postoji isti stil unosa: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trajanje vibracije pritiska"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Glasnoća pritiska tipke"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Odgoda dugog pritiska tipke"</string> <string name="button_default" msgid="3988017840431881491">"Zadano"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Dobro došli u aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s Pisanjem kretnjama"</string> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 72b012b12..93099fee8 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ugyanez a bemenetstílus már létezik: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Gombnyomás rezgési időtartama"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Gombnyomás hangereje"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Hosszú nyomás késleltetése"</string> <string name="button_default" msgid="3988017840431881491">"Alapértelmezett"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Üdvözli a(z) <xliff:g id="APPLICATION_NAME">%s</xliff:g>!"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kézmozdulatokkal történő bevitellel"</string> diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml index 488bcc659..920867df8 100644 --- a/java/res/values-hy-rAM/strings.xml +++ b/java/res/values-hy-rAM/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Մուտքագրման այսպիսի ոճ արդեն գոյություն ունի՝ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Սեղմման թրթռոցի տևողություն"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Սեղմման ձայնի բարձրությունը"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ստեղնի երկար սեղմման ուշացում"</string> <string name="button_default" msgid="3988017840431881491">"Լռելյայնը"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Բարի գալուստ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Ժեստային մուտքագրմամբ"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index 1a67c3e5f..7381cd6fe 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sudah ada gaya masukan yang sama: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durasi getar saat tekan tombol"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume suara saat tekan tombol"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Penundaan tekan lama tombol"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang di <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Ketikan Isyarat"</string> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 473d71285..5572b2cde 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Esiste già uno stile di inuput uguale: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durata vibraz. pressione tasto"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume audio a pressione tasto"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ritardo pressione lunga tasti"</string> <string name="button_default" msgid="3988017840431881491">"Predefinito"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Benvenuto in <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"con la Digitazione gestuale"</string> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index 317282deb..468f7cb9d 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"סגנון קלט זהה כבר קיים: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"משך רטט של לחיצת מקש"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"עוצמת קול של לחיצת מקש"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"השהיית לחיצה ארוכה על מקש"</string> <string name="button_default" msgid="3988017840431881491">"ברירת מחדל"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"ברוך הבא אל <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"עם הקלדת החלקה"</string> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index d3be98be6..5ae8fcce4 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"同じ入力スタイルが既に存在します: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"キー操作バイブの振動時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"キー操作音の音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"キーの長押し時間"</string> <string name="button_default" msgid="3988017840431881491">"デフォルト"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>へようこそ"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"新しいジェスチャー入力をお試しください"</string> diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml index f614ea08a..4f221a937 100644 --- a/java/res/values-ka-rGE/strings.xml +++ b/java/res/values-ka-rGE/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"შეყვანის იგივე სტილი უკვე არსებობს: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"კლავიშზე დაჭერის ვიბრაციის ხანგრძლივობა"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"კლავიშზე დაჭერის ხმა"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"კლავიშზე გრძელი დაჭერის დაყოვნება"</string> <string name="button_default" msgid="3988017840431881491">"ნაგულისხმევი"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"კეთილი იყოს თქვენი მობრძანება <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ში"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ჟესტებით წერით"</string> diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml index e9a29af5f..f1a9ecbd0 100644 --- a/java/res/values-km-rKH/strings.xml +++ b/java/res/values-km-rKH/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"មានរចនាប័ទ្មបញ្ចូលដូចគ្នាដូចហើយ៖ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ថិរវេលាញ័រពេលចុចគ្រាប់ចុច"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"កម្រិតសំឡេងពេលចុចគ្រាប់ចុច"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ពន្យារពេលចុចគ្រាប់ចុចឲ្យយូរ"</string> <string name="button_default" msgid="3988017840431881491">"លំនាំដើម"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"សូមស្វាគមន៍មកកាន់ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ជាមួយការវាយបញ្ចូលដោយប្រើកាយវិការ"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index a778a49b8..1c4612009 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"같은 입력 스타일이 다음과 같이 이미 존재합니다. <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"키를 누를 때 진동 시간"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"키를 누를 때 소리 볼륨"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"키 길게 누르기 지연"</string> <string name="button_default" msgid="3988017840431881491">"기본값"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>에 오신 것을 환영합니다."</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"제스처 타이핑 사용"</string> diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml index bdc99fc9f..55f97c32d 100644 --- a/java/res/values-lo-rLA/strings.xml +++ b/java/res/values-lo-rLA/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ຮູບແບບການປ້ອນຂໍ້ມູນທີ່ຄືກັນມີຢູ່ແລ້ວ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ໄລຍະເວລາຂອງການສັ່ນໃນການກົດປຸ່ມ"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ລະດັບສຽງຂອງການກົດປຸ່ມ"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ໄລຍະເວລາຂອງການກົດປຸ່ມ"</string> <string name="button_default" msgid="3988017840431881491">"ຄ່າເລີ່ມຕົ້ນ"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"ຍິນດີຕ້ອນຮັບສູ່ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ດ້ວຍການພິມແບບ Gesture"</string> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 147d706bb..d1782fa7f 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Toks pat įvesties stilius jau yra: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrav. paspaudus mygt. trukmė"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Garso paspaudus mygt. garsumas"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavišo ilgo paspaudimo delsa"</string> <string name="button_default" msgid="3988017840431881491">"Numatytieji"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Sveiki! Tai „<xliff:g id="APPLICATION_NAME">%s</xliff:g>“"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"naudojant įvestį gestais"</string> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 812cb9a0d..8de28bed6 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Šāds ievades stils jau pastāv: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Taust. nosp. vibrācijas ilgums"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Taustiņu nosp. skaņas skaļums"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Taustiņa ilgās nosp. noildze"</string> <string name="button_default" msgid="3988017840431881491">"Noklusējums"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Laipni lūdzam pakalpojumā <xliff:g id="APPLICATION_NAME">%s</xliff:g>,"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kurā varat izmantot ievadi ar žestiem"</string> diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml index e61584b7e..4b1ef6280 100644 --- a/java/res/values-mn-rMN/strings.xml +++ b/java/res/values-mn-rMN/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ижилхэн оруулах загвар байна: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Товч дарах чичиргээний хугацаа"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Товчны дууны хэмжээ"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Товч удаан дарах хугацааны тохиргоо"</string> <string name="button_default" msgid="3988017840431881491">"Үндсэн"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Та <xliff:g id="APPLICATION_NAME">%s</xliff:g>-д тавтай морилно уу"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Зангаагаар бичихээр"</string> diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml index 07d86c52b..01781a47a 100644 --- a/java/res/values-ms-rMY/strings.xml +++ b/java/res/values-ms-rMY/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tempoh getaran tekan kekunci"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Kelantangan bunyi tekan kekunci"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Kelewatan tekan lama kekunci"</string> <string name="button_default" msgid="3988017840431881491">"Lalai"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Selamat datang ke <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"dengan Taipan Gerak Isyarat"</string> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 268d76a25..d69e1e991 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Inndatastilen finnes allerede: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasjonstid ved tastetrykk"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Lydstyrke ved tastetrykk"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Forsinkelse lange tastetrykk"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Velkommen til <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med Ordføring"</string> diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml index 9f486fdb4..5afe6d19e 100644 --- a/java/res/values-ne-rNP/strings.xml +++ b/java/res/values-ne-rNP/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"यस्तो इनपुट शैली पहिले नै अवस्थित छ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुञ्जी थिचाइ भाइब्रेसन अवधि"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुञ्जी थिचाइ आवाज भोल्युम"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुञ्जी लामो थिचाइ ढिलाइ"</string> <string name="button_default" msgid="3988017840431881491">"पूर्वनिर्धारित"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"तपाईँलाई स्वागत छ<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"इशारा टाइप गर्नेसँग"</string> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index ec5fa06f8..d5406a8e9 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Dezelfde invoerstijl bestaat al: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trilingsduur bij toetsgebruik"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Geluidsvolume bij toetsgebruik"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Vertraging toets lang indrukkn"</string> <string name="button_default" msgid="3988017840431881491">"Standaard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Welkom bij <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"met Invoer met bewegingen"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 0bc3d4b15..30e87bc28 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Taki styl wprowadzania już istnieje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Wibracja przy naciśniętym klawiszu"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Głośność przy naciśniętym klawiszu"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Opóźnienie przy długim naciśnięciu"</string> <string name="button_default" msgid="3988017840431881491">"Domyślne"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Witamy w aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"z pisaniem gestami"</string> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 2c7ff8b40..512647ae6 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Já existe o mesmo estilo de introdução: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duração vibr. ao premir teclas"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume do som ao premir teclas"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Atraso ao manter tecla premida"</string> <string name="button_default" msgid="3988017840431881491">"Predefinido"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bem-vindo(a) a <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"com a Escrita com Gestos"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index f74072228..73ff94ace 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"O estilo de entrada já existe: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duração da vibração ao tocar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume ao tocar na tela"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Atraso ao pressionar teclas"</string> <string name="button_default" msgid="3988017840431881491">"Padrão"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bem-vindo ao <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"com entrada por gestos"</string> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 007c61dfc..a87eeebf3 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Acelaşi stil de introducere există deja: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrare după apăsarea tastei"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Sunet la apăsarea tastelor"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Timpul apăsării lungi a tastei"</string> <string name="button_default" msgid="3988017840431881491">"Prestabilit"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Bun venit la <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"cu Tastarea gestuală"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 15ddb5ebf..3aef94dfc 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такой стиль ввода уже существует: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Вибросигнал при нажатии клавиш"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Звук при нажатии клавиш"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Долгое нажатие"</string> <string name="button_default" msgid="3988017840431881491">"По умолчанию"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Представляем приложение \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\""</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"с непрерывным вводом"</string> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 2f428aee6..01e05004b 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Rovnaký štýl vstupu už existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trvanie vibrov. pri stlač. kl."</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Hlasitosť pri stlačení klávesu"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Oneskor. pri stlač. a podržaní"</string> <string name="button_default" msgid="3988017840431881491">"Predvolené"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Vitajte v aplikácii <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s funkciou Písanie gestami"</string> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 57d5d7268..8872b5656 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Isti slog vnosa že obstaja: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trajanje vibr. ob prit. tipke"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Glasn. zvoka ob pritisku tipke"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Zakasn. za dolg pritisk tipke"</string> <string name="button_default" msgid="3988017840431881491">"Privzeto"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Pozdravljeni v aplikaciji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"s pisanjem s kretnjami"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index de2add40a..0d08933f6 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Исти стил уноса већ постоји: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Трајање вибрације при притиску"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Јачина звука при притиску"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Одлагање при дугом притиску"</string> <string name="button_default" msgid="3988017840431881491">"Подразумевано"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Добро дошли у <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"помоћу Куцања покретима"</string> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index f7a94d92b..f3945a3ad 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Samma indatastil finns redan: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationslängd för tangenter"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ljudvolym för tangenter"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Fördröjning vid långt tryck"</string> <string name="button_default" msgid="3988017840431881491">"Standard"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Välkommen till <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"med svepskrivning"</string> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 5f86d351b..10198ea0a 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Mfumo sawa wa maingizo tayari upo: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Bonyeza kitufe cha muda wa kutetema"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Bonyeza kitufe cha kiwango cha sauti"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ubofyaji kitufe kunakochelewa"</string> <string name="button_default" msgid="3988017840431881491">"Chaguo-msingi"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Karibu kwenye <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"kwa Kuandika kwa ishara"</string> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 94374cf1a..674c03b06 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"รูปแบบการป้อนข้อมูลเดียวกันนี้มีอยู่แล้ว: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ระยะเวลาการสั่นเมื่อกดแป้นพิมพ์"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ระดับเสียงเมื่อกดแป้นพิมพ์"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"การหน่วงเวลาของการกดแป้นค้าง"</string> <string name="button_default" msgid="3988017840431881491">"ค่าเริ่มต้น"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"ยินดีต้อนรับสู่ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"พร้อมการป้อนข้อมูลด้วยท่าทาง"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index d523e2e46..6fa06c913 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Umiiral na ang parehong estilo ng input: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tagal ng vibration ng keypress"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume ng tunog ng keypress"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="button_default" msgid="3988017840431881491">"Default"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Maligayang pagdating sa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"gamit ang Gesture na Pag-type"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 1db5ec182..5802d7960 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Aynı giriş stili zaten var: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tuşa basma titreşim süresi"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tuşa basma ses seviyesi"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tuşa uzun basma gecikmesi"</string> <string name="button_default" msgid="3988017840431881491">"Varsayılan"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> uygulamasına hoş geldiniz"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Hareketle Yazmayı içerir"</string> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 29003d1be..639658d3e 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такий стиль введення вже існує: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Вібрація при натисканні клавіш"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Гучність натискання клавіш"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Затримка довгого натискання"</string> <string name="button_default" msgid="3988017840431881491">"За умовчанням"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Вітаємо в програмі <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"з функцією Ввід жестами"</string> diff --git a/java/res/values-v21/themes-lxx.xml b/java/res/values-v21/themes-lxx.xml new file mode 100644 index 000000000..5a6017cce --- /dev/null +++ b/java/res/values-v21/themes-lxx.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style + name="InputView.LXX" + parent="InputView" + > + <item name="android:elevation">8dp</item> + </style> +</resources> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 08d5eee17..3c803a366 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Đã tồn tại kiểu nhập tương tự: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Thời gian rung khi nhấn phím"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Âm lượng khi nhấn phím"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tgian chờ cho nhấn và giữ phím"</string> <string name="button_default" msgid="3988017840431881491">"Mặc định"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Chào mừng bạn đến với <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"với Nhập bằng cử chỉ"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 7e0a103f4..7524e324c 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已经存在相同的输入风格:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按键振动时长"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按键音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"按键长按延迟"</string> <string name="button_default" msgid="3988017840431881491">"默认"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"欢迎使用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"体验顺畅的滑行输入体验"</string> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index 74a552421..3c013ad4f 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"長按鍵延遲"</string> <string name="button_default" msgid="3988017840431881491">"預設"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"歡迎使用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"配備觸控輸入功能"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 3b999eaf5..28bbc8a46 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動持續時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"按鍵長按延遲"</string> <string name="button_default" msgid="3988017840431881491">"預設"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"歡迎使用 <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"含滑行輸入功能"</string> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 4d2afab9b..0c80aee61 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -124,6 +124,7 @@ <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Isitayela sokufaka esifanayo sesivele sikhona: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Ubude besikhathi sokudlidliza ukucindezela ukhiye"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ivolumu yomsindo wokucindezela ukhiye"</string> + <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ukulibazisa ukucindezela isikhashana ukhiye"</string> <string name="button_default" msgid="3988017840431881491">"Okuzenzakalelayo"</string> <string name="setup_welcome_title" msgid="6112821709832031715">"Siyakwamukela ku-<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="setup_welcome_additional_description" msgid="8150252008545768953">"nokuthayipha ngokuthinta"</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index f2072fd37..be35d131d 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -20,6 +20,8 @@ <resources> <declare-styleable name="KeyboardTheme"> + <!-- InputView style --> + <attr name="inputViewStyle" format="reference" /> <!-- Keyboard style --> <attr name="keyboardStyle" format="reference" /> <!-- KeyboardView style --> diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml index 199f9772b..cc8c1a01d 100644 --- a/java/res/values/donottranslate-debug-settings.xml +++ b/java/res/values/donottranslate-debug-settings.xml @@ -28,8 +28,6 @@ <string name="sliding_key_input_preview">Show slide indicator</string> <!-- Option summary to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=66]--> <string name="sliding_key_input_preview_summary">Display visual cue while sliding from Shift or Symbol keys</string> - <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> - <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> <!-- Title of the settings for customize key popup animation parameters [CHAR LIMIT=35] --> <string name="prefs_customize_key_preview_animation">Customize key preview animation</string> <!-- Title of the settings for key popup show up animation duration (in milliseconds) [CHAR LIMIT=35] --> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 6aea637c5..54bfc51d3 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -56,9 +56,11 @@ <!-- Option for enabling or disabling the split keyboard layout. [CHAR LIMIT=65]--> <string name="enable_split_keyboard">Enable split keyboard</string> - <string name="sync_now_title" translatable="false">Sync Now</string> - <string name="sync_now_summary" translatable="false">Sync your personal dictionary</string> - <string name="sync_now_summary_disabled_signed_out" translatable="false">Select an account to enable sync</string> + <!-- TODO: Enable translation for user-visible strings --> + <string name="cloud_sync_title" translatable="false">Enable sync</string> + <string name="cloud_sync_summary" translatable="false">Sync your personal dictionary across devices</string> + <string name="cloud_sync_summary_disabled_signed_out" translatable="false">Select an account to enable sync</string> + <string name="sync_now_title" translatable="false">[DEBUG] Sync Now</string> <!-- Option name for including other IMEs in the language switch list [CHAR LIMIT=30] --> <string name="include_other_imes_in_language_switch_list">Switch to other input methods</string> @@ -359,6 +361,8 @@ mobile devices. [CHAR LIMIT=25] --> <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration</string> <!-- Title of the settings for keypress sound volume [CHAR LIMIT=35] --> <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string> + <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> + <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] --> <string name="button_default">Default</string> diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml index 110f6b792..f7cb10f93 100644 --- a/java/res/values/themes-common.xml +++ b/java/res/values/themes-common.xml @@ -20,6 +20,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="KeyboardIcons" /> + <style name="InputView" /> <!-- Default theme values --> <style name="Keyboard"> <item name="rowHeight">25%p</item> diff --git a/java/res/values/themes-holo.xml b/java/res/values/themes-holo.xml index 9f1bd2f78..efac65627 100644 --- a/java/res/values/themes-holo.xml +++ b/java/res/values/themes-holo.xml @@ -19,6 +19,10 @@ --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style + name="InputView.Holo" + parent="InputView" + /> <!-- Holo KeyboardView theme (ICS and KLP) --> <style name="KeyboardView.Holo" diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index bfbac0a94..ecf40e4e6 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -20,6 +20,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="KeyboardTheme.ICS" parent="KeyboardIcons.Holo"> + <item name="inputViewStyle">@style/InputView.Holo</item> <item name="keyboardStyle">@style/Keyboard.ICS</item> <item name="keyboardViewStyle">@style/KeyboardView.ICS</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.ICS</item> diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml index 36b1fc117..de1cd9bf1 100644 --- a/java/res/values/themes-klp.xml +++ b/java/res/values/themes-klp.xml @@ -20,6 +20,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="KeyboardTheme.KLP" parent="KeyboardIcons.Holo"> + <item name="inputViewStyle">@style/InputView.Holo</item> <item name="keyboardStyle">@style/Keyboard.KLP</item> <item name="keyboardViewStyle">@style/KeyboardView.KLP</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.KLP</item> diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml index 67f94f329..b081772e9 100644 --- a/java/res/values/themes-lxx-dark.xml +++ b/java/res/values/themes-lxx-dark.xml @@ -20,6 +20,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="KeyboardTheme.LXX_Dark" parent="KeyboardIcons.LXX_Dark"> + <item name="inputViewStyle">@style/InputView.LXX</item> <item name="keyboardStyle">@style/Keyboard.LXX_Dark</item> <item name="keyboardViewStyle">@style/KeyboardView.LXX_Dark</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.LXX_Dark</item> diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml index be817f46a..3d294e450 100644 --- a/java/res/values/themes-lxx-light.xml +++ b/java/res/values/themes-lxx-light.xml @@ -20,6 +20,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="KeyboardTheme.LXX_Light" parent="KeyboardIcons.LXX_Light"> + <item name="inputViewStyle">@style/InputView.LXX</item> <item name="keyboardStyle">@style/Keyboard.LXX_Light</item> <item name="keyboardViewStyle">@style/KeyboardView.LXX_Light</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.LXX_Light</item> diff --git a/java/res/values/themes-lxx.xml b/java/res/values/themes-lxx.xml index c72188871..463306b43 100644 --- a/java/res/values/themes-lxx.xml +++ b/java/res/values/themes-lxx.xml @@ -19,6 +19,10 @@ --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style + name="InputView.LXX" + parent="InputView" + /> <!-- LXX KeyboardView theme (LXX_Light and LXX_Dark) --> <style name="KeyboardView.LXX" diff --git a/java/res/xml/prefs_screen_accounts.xml b/java/res/xml/prefs_screen_accounts.xml index 003a37ff7..41642bf08 100644 --- a/java/res/xml/prefs_screen_accounts.xml +++ b/java/res/xml/prefs_screen_accounts.xml @@ -28,7 +28,15 @@ android:title="@string/switch_accounts" android:summary="@string/no_accounts_selected" /> - <!-- title will be set programmatically to embed application name --> + <!-- Summary will be set programmatically to reflect the account status --> + <CheckBoxPreference + android:key="pref_enable_cloud_sync" + android:title="@string/cloud_sync_title" + android:defaultValue="false" + android:persistent="true" + android:disableDependentsState="false" /> + + <!-- Title will be set programmatically to embed application name --> <CheckBoxPreference android:key="pref_enable_metrics_logging" android:summary="@string/enable_metrics_logging_summary" @@ -38,5 +46,6 @@ <!-- This preference (acts like a button) enables the user to initiate an one time sync. --> <Preference android:key="pref_beanstalk" android:persistent="false" - android:title="@string/sync_now_title" /> + android:title="@string/sync_now_title" + android:dependency="pref_enable_cloud_sync" /> </PreferenceScreen> diff --git a/java/res/xml/prefs_screen_advanced.xml b/java/res/xml/prefs_screen_advanced.xml index 5aefcc8d5..329822093 100644 --- a/java/res/xml/prefs_screen_advanced.xml +++ b/java/res/xml/prefs_screen_advanced.xml @@ -31,6 +31,12 @@ android:key="pref_keypress_sound_volume" android:title="@string/prefs_keypress_sound_volume_settings" latin:maxValue="100" /> <!-- percent --> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:key="pref_key_longpress_timeout" + android:title="@string/prefs_key_longpress_timeout_settings" + latin:minValue="@integer/config_min_longpress_timeout" + latin:maxValue="@integer/config_max_longpress_timeout" + latin:stepValue="@integer/config_longpress_timeout_step" /> <!-- The settings for showing setup wizard application icon shouldn't be persistent and the default value is added programmatically. --> <CheckBoxPreference diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml index 25f7c6612..486d2367e 100644 --- a/java/res/xml/prefs_screen_debug.xml +++ b/java/res/xml/prefs_screen_debug.xml @@ -46,12 +46,6 @@ android:summary="@string/sliding_key_input_preview_summary" android:defaultValue="true" android:persistent="true" /> - <com.android.inputmethod.latin.settings.SeekBarDialogPreference - android:key="pref_key_longpress_timeout" - android:title="@string/prefs_key_longpress_timeout_settings" - latin:minValue="@integer/config_min_longpress_timeout" - latin:maxValue="@integer/config_max_longpress_timeout" - latin:stepValue="@integer/config_longpress_timeout_step" /> <CheckBoxPreference android:key="pref_has_custom_key_preview_animation_params" android:title="@string/prefs_customize_key_preview_animation" diff --git a/java/res/xml/rowkeys_telugu2.xml b/java/res/xml/rowkeys_telugu2.xml index a472fd3f8..f1ce459e5 100644 --- a/java/res/xml/rowkeys_telugu2.xml +++ b/java/res/xml/rowkeys_telugu2.xml @@ -51,10 +51,10 @@ latin:moreKeys="ఫ" /> <!-- U+0C30: "ర" TELUGU LETTER RA U+0C31: "ఱ" TELUGU LETTER RRA - U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R --> + U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA --> <Key latin:keySpec="ర" - latin:moreKeys="ఱ,ృ" /> + latin:moreKeys="ఱ,్ర" /> <!-- U+0C15: "క" TELUGU LETTER KA U+0C16: "ఖ" TELUGU LETTER KHA --> <Key diff --git a/java/res/xml/rowkeys_telugu3.xml b/java/res/xml/rowkeys_telugu3.xml index 05755ec4d..2e3730ac7 100644 --- a/java/res/xml/rowkeys_telugu3.xml +++ b/java/res/xml/rowkeys_telugu3.xml @@ -19,24 +19,28 @@ --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> - <!-- U+0C46: "ె" TELUGU VOWEL SIGN E + <!-- U+0C4A: "ొ" TELUGU VOWEL SIGN O U+0C12: "ఒ" TELUGU LETTER O --> <Key - latin:keySpec="ె" + latin:keySpec="ొ" latin:moreKeys="ఒ" /> - <!-- U+0C02: "ం" TELUGU SIGN ANUSVARA + <!-- U+0C46: "ె" TELUGU VOWEL SIGN E U+0C0E: "ఎ" TELUGU LETTER E --> <Key - latin:keySpec="ం" + latin:keySpec="ె" latin:moreKeys="ఎ" /> - <!-- U+0C2E: "మ" TELUGU LETTER MA --> - <Key latin:keySpec="మ" /> + <!-- U+0C2E: "మ" TELUGU LETTER MA + U+0C02: "ం" TELUGU SIGN ANUSVARA + U+0C01: "ఁ" TELUGU SIGN CANDRABINDU --> + <Key latin:keySpec="మ" + latin:moreKeys="ం,ఁ" /> <!-- U+0C28: "న" TELUGU LETTER NA U+0C23: "ణ" TELUGU LETTER NNA - U+0C19: "ఙ" TELUGU LETTER NGA --> + U+0C19: "ఙ" TELUGU LETTER NGA + U+0C1E: "ఞ" TELUGU LETTER NYA --> <Key latin:keySpec="న" - latin:moreKeys="ణ,ఙ" /> + latin:moreKeys="ణ,ఙ,ఞ" /> <!-- U+0C35: "వ" TELUGU LETTER VA --> <Key latin:keySpec="వ" /> <!-- U+0C32: "ల" TELUGU LETTER LA @@ -50,10 +54,10 @@ latin:keySpec="స" latin:moreKeys="శ" /> <!-- U+0C0B: "ఋ" TELUGU LETTER VOCALIC R - U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA --> + U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R --> <Key latin:keySpec="ఋ" - latin:moreKeys="్ర" /> + latin:moreKeys="ృ" /> <!-- U+0C37: "ష" TELUGU LETTER SSA U+0C15/U+0C4D/U+0C37: "క్ష" TELUGU LETTER KA/TELUGU SIGN VIRAMA/TELUGU LETTER SSA --> <Key diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java index 5af31795c..f4f54b624 100644 --- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java @@ -16,13 +16,20 @@ package com.android.inputmethod.compat; +import android.annotation.TargetApi; import android.graphics.Matrix; import android.graphics.RectF; +import android.os.Build; +import android.view.inputmethod.CursorAnchorInfo; -import com.android.inputmethod.annotations.UsedForTesting; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; -@UsedForTesting -public final class CursorAnchorInfoCompatWrapper { +/** + * A wrapper for {@link CursorAnchorInfo}, which has been introduced in API Level 21. You can use + * this wrapper to avoid direct dependency on newly introduced types. + */ +public class CursorAnchorInfoCompatWrapper { /** * The insertion marker or character bounds have at least one visible region. @@ -39,123 +46,138 @@ public final class CursorAnchorInfoCompatWrapper { */ public static final int FLAG_IS_RTL = 0x04; - // Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX). - private static final CompatUtils.ClassWrapper sCursorAnchorInfoClass; - private static final CompatUtils.ToIntMethodWrapper sGetSelectionStartMethod; - private static final CompatUtils.ToIntMethodWrapper sGetSelectionEndMethod; - private static final CompatUtils.ToObjectMethodWrapper<RectF> sGetCharacterBoundsMethod; - private static final CompatUtils.ToIntMethodWrapper sGetCharacterBoundsFlagsMethod; - private static final CompatUtils.ToObjectMethodWrapper<CharSequence> sGetComposingTextMethod; - private static final CompatUtils.ToIntMethodWrapper sGetComposingTextStartMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBaselineMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBottomMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerHorizontalMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerTopMethod; - private static final CompatUtils.ToObjectMethodWrapper<Matrix> sGetMatrixMethod; - private static final CompatUtils.ToIntMethodWrapper sGetInsertionMarkerFlagsMethod; - - private static int INVALID_TEXT_INDEX = -1; - static { - sCursorAnchorInfoClass = CompatUtils.getClassWrapper( - "android.view.inputmethod.CursorAnchorInfo"); - sGetSelectionStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getSelectionStart", INVALID_TEXT_INDEX); - sGetSelectionEndMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getSelectionEnd", INVALID_TEXT_INDEX); - sGetCharacterBoundsMethod = sCursorAnchorInfoClass.getMethod( - "getCharacterBounds", (RectF)null, int.class); - sGetCharacterBoundsFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getCharacterBoundsFlags", 0, int.class); - sGetComposingTextMethod = sCursorAnchorInfoClass.getMethod( - "getComposingText", (CharSequence)null); - sGetComposingTextStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getComposingTextStart", INVALID_TEXT_INDEX); - sGetInsertionMarkerBaselineMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerBaseline", 0.0f); - sGetInsertionMarkerBottomMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerBottom", 0.0f); - sGetInsertionMarkerHorizontalMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerHorizontal", 0.0f); - sGetInsertionMarkerTopMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerTop", 0.0f); - sGetMatrixMethod = sCursorAnchorInfoClass.getMethod("getMatrix", (Matrix)null); - sGetInsertionMarkerFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerFlags", 0); - } - - @UsedForTesting - public boolean isAvailable() { - return sCursorAnchorInfoClass.exists() && mInstance != null; - } - - private Object mInstance; - - private CursorAnchorInfoCompatWrapper(final Object instance) { - mInstance = instance; + private CursorAnchorInfoCompatWrapper() { + // This class is not publicly instantiable. } - @UsedForTesting - public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) { - if (!sCursorAnchorInfoClass.exists()) { - return new CursorAnchorInfoCompatWrapper(null); + @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @Nullable + public static CursorAnchorInfoCompatWrapper wrap(@Nullable final CursorAnchorInfo instance) { + if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + return null; } - return new CursorAnchorInfoCompatWrapper(instance); - } - - private static final class FakeHolder { - static CursorAnchorInfoCompatWrapper sInstance = new CursorAnchorInfoCompatWrapper(null); - } - - @UsedForTesting - public static CursorAnchorInfoCompatWrapper getFake() { - return FakeHolder.sInstance; + if (instance == null) { + return null; + } + return new RealWrapper(instance); } public int getSelectionStart() { - return sGetSelectionStartMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public int getSelectionEnd() { - return sGetSelectionEndMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public CharSequence getComposingText() { - return sGetComposingTextMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public int getComposingTextStart() { - return sGetComposingTextStartMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public Matrix getMatrix() { - return sGetMatrixMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public RectF getCharacterBounds(final int index) { - return sGetCharacterBoundsMethod.invoke(mInstance, index); + throw new UnsupportedOperationException("not supported."); } public int getCharacterBoundsFlags(final int index) { - return sGetCharacterBoundsFlagsMethod.invoke(mInstance, index); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerBaseline() { - return sGetInsertionMarkerBaselineMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerBottom() { - return sGetInsertionMarkerBottomMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerHorizontal() { - return sGetInsertionMarkerHorizontalMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerTop() { - return sGetInsertionMarkerTopMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public int getInsertionMarkerFlags() { - return sGetInsertionMarkerFlagsMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); + } + + @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + private static final class RealWrapper extends CursorAnchorInfoCompatWrapper { + + @Nonnull + private final CursorAnchorInfo mInstance; + + public RealWrapper(@Nonnull final CursorAnchorInfo info) { + mInstance = info; + } + + @Override + public int getSelectionStart() { + return mInstance.getSelectionStart(); + } + + @Override + public int getSelectionEnd() { + return mInstance.getSelectionEnd(); + } + + @Override + public CharSequence getComposingText() { + return mInstance.getComposingText(); + } + + @Override + public int getComposingTextStart() { + return mInstance.getComposingTextStart(); + } + + @Override + public Matrix getMatrix() { + return mInstance.getMatrix(); + } + + @Override + public RectF getCharacterBounds(final int index) { + return mInstance.getCharacterBounds(index); + } + + @Override + public int getCharacterBoundsFlags(final int index) { + return mInstance.getCharacterBoundsFlags(index); + } + + @Override + public float getInsertionMarkerBaseline() { + return mInstance.getInsertionMarkerBaseline(); + } + + @Override + public float getInsertionMarkerBottom() { + return mInstance.getInsertionMarkerBottom(); + } + + @Override + public float getInsertionMarkerHorizontal() { + return mInstance.getInsertionMarkerHorizontal(); + } + + @Override + public float getInsertionMarkerTop() { + return mInstance.getInsertionMarkerTop(); + } + + @Override + public int getInsertionMarkerFlags() { + return mInstance.getInsertionMarkerFlags(); + } } } diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java index 0f00be133..16260ab6a 100644 --- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java @@ -31,9 +31,6 @@ public final class ViewCompatUtils { private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod( View.class, "setPaddingRelative", int.class, int.class, int.class, int.class); - // Note that View.setElevation(float) has been introduced in API level 21. - private static final Method METHOD_setElevation = CompatUtils.getMethod( - View.class, "setElevation", float.class); // Note that View.setTextAlignment(int) has been introduced in API level 17. private static final Method METHOD_setTextAlignment = CompatUtils.getMethod( View.class, "setTextAlignment", int.class); @@ -58,10 +55,6 @@ public final class ViewCompatUtils { CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom); } - public static void setElevation(final View view, final float elevation) { - CompatUtils.invoke(view, null, METHOD_setElevation, elevation); - } - // These TEXT_ALIGNMENT_* constants have been introduced in API 17. public static final int TEXT_ALIGNMENT_INHERIT = 0; public static final int TEXT_ALIGNMENT_GRAVITY = 1; diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java new file mode 100644 index 000000000..52b8b74e8 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.inputmethodservice.InputMethodService; +import android.view.View; + +public class ViewOutlineProviderCompatUtils { + private ViewOutlineProviderCompatUtils() { + // This utility class is not publicly instantiable. + } + + public interface InsetsUpdater { + public void setInsets(final InputMethodService.Insets insets); + } + + private static final InsetsUpdater EMPTY_INSETS_UPDATER = new InsetsUpdater() { + @Override + public void setInsets(final InputMethodService.Insets insets) {} + }; + + public static InsetsUpdater setInsetsOutlineProvider(final View view) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + return EMPTY_INSETS_UPDATER; + } + return ViewOutlineProviderCompatUtilsLXX.setInsetsOutlineProvider(view); + } +} diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java new file mode 100644 index 000000000..5bbb5ce99 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.annotation.TargetApi; +import android.graphics.Outline; +import android.inputmethodservice.InputMethodService; +import android.os.Build; +import android.view.View; +import android.view.ViewOutlineProvider; + +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +class ViewOutlineProviderCompatUtilsLXX { + private ViewOutlineProviderCompatUtilsLXX() { + // This utility class is not publicly instantiable. + } + + static InsetsUpdater setInsetsOutlineProvider(final View view) { + final InsetsOutlineProvider provider = new InsetsOutlineProvider(view); + view.setOutlineProvider(provider); + return provider; + } + + private static class InsetsOutlineProvider extends ViewOutlineProvider + implements InsetsUpdater { + private final View mView; + private static final int NO_DATA = -1; + private int mLastVisibleTopInsets = NO_DATA; + + public InsetsOutlineProvider(final View view) { + mView = view; + view.setOutlineProvider(this); + } + + @Override + public void setInsets(final InputMethodService.Insets insets) { + final int visibleTopInsets = insets.visibleTopInsets; + if (mLastVisibleTopInsets != visibleTopInsets) { + mLastVisibleTopInsets = visibleTopInsets; + mView.invalidateOutline(); + } + } + + @Override + public void getOutline(final View view, final Outline outline) { + if (mLastVisibleTopInsets == NO_DATA) { + // Call default implementation. + ViewOutlineProvider.BACKGROUND.getOutline(view, outline); + return; + } + // TODO: Revisit this when floating/resize keyboard is supported. + outline.setRect( + view.getLeft(), mLastVisibleTopInsets, view.getRight(), view.getBottom()); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 52b9284be..1c66c37d3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -28,6 +28,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.Xml; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; @@ -39,6 +40,7 @@ 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.DebugLogUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -313,23 +315,78 @@ public final class KeyboardLayoutSet { 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) { + final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX + + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); + final int xmlId = getXmlId(resources, layoutSetName); + final XmlResourceParser parser = resources.getXml(xmlId); + try { + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + // Bovinate through the XML stupidly searching for TAG_FEATURE, and read + // the script Id from it. + parser.next(); + final String tag = parser.getName(); + if (TAG_FEATURE.equals(tag)) { + return readScriptIdFromTagFeature(resources, parser); + } + } + } catch (final IOException | XmlPullParserException e) { + throw new RuntimeException(e.getMessage() + " in " + layoutSetName, e); + } finally { + parser.close(); + } + // If the tag is not found, then the default script is Latin. + return ScriptUtils.SCRIPT_LATIN; + } + + private static int readScriptIdFromTagFeature(final Resources resources, + final XmlPullParser parser) throws IOException, XmlPullParserException { + final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.KeyboardLayoutSet_Feature); + try { + final int scriptId = + featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript, + ScriptUtils.SCRIPT_UNKNOWN); + XmlParseUtils.checkEndTag(TAG_FEATURE, parser); + return scriptId; + } finally { + featureAttr.recycle(); + } + } + public KeyboardLayoutSet build() { if (mParams.mSubtype == null) throw new RuntimeException("KeyboardLayoutSet subtype is not specified"); - final String packageName = mResources.getResourcePackageName( - R.xml.keyboard_layout_set_qwerty); - final String keyboardLayoutSetName = mParams.mKeyboardLayoutSetName; - final int xmlId = mResources.getIdentifier(keyboardLayoutSetName, "xml", packageName); + final int xmlId = getXmlId(mResources, mParams.mKeyboardLayoutSetName); try { parseKeyboardLayoutSet(mResources, xmlId); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e); - } catch (final XmlPullParserException e) { - throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e); + } catch (final IOException | XmlPullParserException e) { + throw new RuntimeException(e.getMessage() + " in " + mParams.mKeyboardLayoutSetName, + e); } return new KeyboardLayoutSet(mContext, mParams); } + private static int getXmlId(final Resources resources, final String keyboardLayoutSetName) { + final String packageName = resources.getResourcePackageName( + R.xml.keyboard_layout_set_qwerty); + return resources.getIdentifier(keyboardLayoutSetName, "xml", packageName); + } + private void parseKeyboardLayoutSet(final Resources res, final int resId) throws XmlPullParserException, IOException { final XmlResourceParser parser = res.getXml(resId); @@ -407,17 +464,8 @@ public final class KeyboardLayoutSet { private void parseKeyboardLayoutSetFeature(final XmlPullParser parser) throws XmlPullParserException, IOException { - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.KeyboardLayoutSet_Feature); - try { - final int scriptId = a.getInt( - R.styleable.KeyboardLayoutSet_Feature_supportedScript, - ScriptUtils.SCRIPT_LATIN); - XmlParseUtils.checkEndTag(TAG_FEATURE, parser); - setScriptId(scriptId); - } finally { - a.recycle(); - } + final int scriptId = readScriptIdFromTagFeature(mResources, parser); + setScriptId(scriptId); } private static int getKeyboardMode(final EditorInfo editorInfo) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 6d8c8b76f..8a9688ac4 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -22,7 +22,6 @@ import android.os.Build.VERSION_CODES; import android.preference.PreferenceManager; import android.util.Log; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.R; @@ -45,7 +44,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES; - @UsedForTesting + /* package private for testing */ static final KeyboardTheme[] KEYBOARD_THEMES = { new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS, // This has never been selected because we support ICS or later. @@ -57,6 +56,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { // Default theme for LXX. BuildCompatUtils.VERSION_CODES_LXX), new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark, + // This has never been selected as default theme. VERSION_CODES.BASE), }; @@ -68,7 +68,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { public final int mThemeId; public final int mStyleId; public final String mThemeName; - private final int mMinApiVersion; + public final int mMinApiVersion; // Note: The themeId should be aligned with "themeId" attribute of Keyboard style // in values/themes-<style>.xml. @@ -98,7 +98,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return mThemeId; } - @UsedForTesting + /* package private for testing */ static KeyboardTheme searchKeyboardThemeById(final int themeId, final KeyboardTheme[] availableThemeIds) { // TODO: This search algorithm isn't optimal if there are many themes. @@ -110,7 +110,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return null; } - @UsedForTesting + /* package private for testing */ static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, final int sdkVersion, final KeyboardTheme[] availableThemeArray) { final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null); @@ -150,7 +150,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { saveKeyboardThemeId(themeId, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } - @UsedForTesting + /* package private for testing */ static String getPreferenceKey(final int sdkVersion) { if (sdkVersion <= VERSION_CODES.KITKAT) { return KLP_KEYBOARD_THEME_KEY; @@ -158,7 +158,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return LXX_KEYBOARD_THEME_KEY; } - @UsedForTesting + /* package private for testing */ static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs, final int sdkVersion) { final String prefKey = getPreferenceKey(sdkVersion); @@ -171,6 +171,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray); } + /* package private for testing */ static KeyboardTheme[] getAvailableThemeArray(final Context context) { if (AVAILABLE_KEYBOARD_THEMES == null) { final int[] availableThemeIdStringArray = context.getResources().getIntArray( @@ -184,11 +185,12 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray( new KeyboardTheme[availableThemeList.size()]); + Arrays.sort(AVAILABLE_KEYBOARD_THEMES); } return AVAILABLE_KEYBOARD_THEMES; } - @UsedForTesting + /* package private for testing */ static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion, final KeyboardTheme[] availableThemeArray) { final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index e7be6de4c..06f9ced92 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -28,6 +28,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Typeface; import android.preference.PreferenceManager; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; @@ -57,8 +58,10 @@ import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; +import java.util.Locale; import java.util.WeakHashMap; /** @@ -855,8 +858,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private String layoutLanguageOnSpacebar(final Paint paint, final RichInputMethodSubtype subtype, final int width) { if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_MULTIPLE) { - // TODO: return an appropriate string - return ""; + final Locale[] locales = subtype.getLocales(); + final String[] languages = new String[locales.length]; + for (int i = 0; i < locales.length; ++i) { + languages[i] = StringUtils.toUpperCaseOfStringForLocale( + locales[i].getLanguage(), true /* needsToUpperCase */, Locale.ROOT); + } + return TextUtils.join(" / ", languages); } // Choose appropriate language name to fit into the width. diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java index c22717f95..2b7d2ce3c 100644 --- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java +++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java @@ -30,6 +30,7 @@ import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * A controller class of the add-to-dictionary indicator (a.k.a. TextDecorator). This class @@ -56,6 +57,7 @@ public class TextDecorator { private String mWaitingWord = null; private int mWaitingCursorStart = INVALID_CURSOR_INDEX; private int mWaitingCursorEnd = INVALID_CURSOR_INDEX; + @Nullable private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null; @Nonnull @@ -150,7 +152,7 @@ public class TextDecorator { * mode.</p> * @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}. */ - public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) { + public void onUpdateCursorAnchorInfo(@Nullable final CursorAnchorInfoCompatWrapper info) { mCursorAnchorInfoWrapper = info; // Do not use layoutLater() to minimize the latency. layoutImmediately(); @@ -182,7 +184,7 @@ public class TextDecorator { private void layoutMain() { final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper; - if (info == null || !info.isAvailable()) { + if (info == null) { cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available."); return; } @@ -301,7 +303,7 @@ public class TextDecorator { */ private static final class LayoutInvalidator { private final HandlerImpl mHandler; - public LayoutInvalidator(final TextDecorator ownerInstance) { + public LayoutInvalidator(@Nonnull final TextDecorator ownerInstance) { mHandler = new HandlerImpl(ownerInstance); } @@ -309,7 +311,7 @@ public class TextDecorator { private static final class HandlerImpl extends LeakGuardHandlerWrapper<TextDecorator> { - public HandlerImpl(final TextDecorator ownerInstance) { + public HandlerImpl(@Nonnull final TextDecorator ownerInstance) { super(ownerInstance); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java index 4f8a105d5..1a55359f5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java @@ -23,6 +23,8 @@ import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; +import javax.annotation.Nonnull; + // TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so. public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { public interface Callbacks { @@ -34,7 +36,7 @@ public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { private static final int MSG_DISMISS_KEY_PREVIEW = 0; private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; - public DrawingHandler(final Callbacks ownerInstance) { + public DrawingHandler(@Nonnull final Callbacks ownerInstance) { super(ownerInstance); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java index ec7b9b024..80b299bf5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java @@ -27,6 +27,8 @@ import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; +import javax.annotation.Nonnull; + // TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so. public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy { public interface Callbacks { @@ -45,7 +47,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple private final int mIgnoreAltCodeKeyTimeout; private final int mGestureRecognitionUpdateTime; - public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, + public TimerHandler(@Nonnull final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime) { super(ownerInstance); mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout; diff --git a/java/src/com/android/inputmethod/latin/BackupAgent.java b/java/src/com/android/inputmethod/latin/BackupAgent.java index 1f044618a..b2d92b30c 100644 --- a/java/src/com/android/inputmethod/latin/BackupAgent.java +++ b/java/src/com/android/inputmethod/latin/BackupAgent.java @@ -17,15 +17,41 @@ package com.android.inputmethod.latin; import android.app.backup.BackupAgentHelper; +import android.app.backup.BackupDataInput; import android.app.backup.SharedPreferencesBackupHelper; +import android.content.SharedPreferences; +import android.os.ParcelFileDescriptor; + +import com.android.inputmethod.latin.settings.LocalSettingsConstants; + +import java.io.IOException; /** - * Backs up the Latin IME shared preferences. + * Backup/restore agent for LatinIME. + * Currently it backs up the default shared preferences. */ public final class BackupAgent extends BackupAgentHelper { + private static final String PREF_SUFFIX = "_preferences"; + @Override public void onCreate() { addHelper("shared_pref", new SharedPreferencesBackupHelper(this, - getPackageName() + "_preferences")); + getPackageName() + PREF_SUFFIX)); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + // Let the restore operation go through + super.onRestore(data, appVersionCode, newState); + + // Remove the preferences that we don't want restored. + final SharedPreferences.Editor prefEditor = getSharedPreferences( + getPackageName() + PREF_SUFFIX, MODE_PRIVATE).edit(); + for (final String key : LocalSettingsConstants.PREFS_TO_SKIP_RESTORING) { + prefEditor.remove(key); + } + // Flush the changes to disk. + prefEditor.commit(); } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 10dea749d..2fece7c85 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -70,7 +70,7 @@ public final class BinaryDictionary extends Dictionary { private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 5; private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0; private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1; - private static final int FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX = 2; + private static final int FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX = 2; private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3; private static final int FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX = 4; @@ -179,9 +179,10 @@ public final class BinaryDictionary extends Dictionary { boolean[] isBeginningOfSentenceArray, int[] word); private static native void getWordPropertyNative(long dict, int[] word, boolean isBeginningOfSentence, int[] outCodePoints, boolean[] outFlags, - int[] outProbabilityInfo, ArrayList<int[]> outBigramTargets, - ArrayList<int[]> outBigramProbabilityInfo, ArrayList<int[]> outShortcutTargets, - ArrayList<Integer> outShortcutProbabilities); + int[] outProbabilityInfo, ArrayList<int[][]> outNgramPrevWordsArray, + ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray, + ArrayList<int[]> outNgramTargets, ArrayList<int[]> outNgramProbabilityInfo, + ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities); private static native int getNextWordNative(long dict, int token, int[] outCodePoints, boolean[] outIsBeginningOfSentence); private static native void getSuggestionsNative(long dict, long proximityInfo, @@ -201,8 +202,7 @@ public final class BinaryDictionary extends Dictionary { int[] word, int probability, int timestamp); private static native boolean removeNgramEntryNative(long dict, int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word); - // TODO: Rename to updateEntriesForWordWithNgramContextNative. - private static native boolean updateCounterNative(long dict, + private static native boolean updateEntriesForWordWithNgramContextNative(long dict, int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word, boolean isValidWord, int count, int timestamp); private static native int addMultipleDictionaryEntriesNative(long dict, @@ -292,6 +292,7 @@ public final class BinaryDictionary extends Dictionary { settingsValuesForSuggestion.mSpaceAwareGestureEnabled); session.mNativeSuggestOptions.setAdditionalFeaturesOptions( settingsValuesForSuggestion.mAdditionalFeaturesSettingValues); + session.mNativeSuggestOptions.setWeightForLocale(weightForLocale); if (inOutWeightOfLangModelVsSpatialModel != null) { session.mInputOutputWeightOfLangModelVsSpatialModel[0] = inOutWeightOfLangModelVsSpatialModel[0]; @@ -388,20 +389,25 @@ public final class BinaryDictionary extends Dictionary { final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT]; final int[] outProbabilityInfo = new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT]; - final ArrayList<int[]> outBigramTargets = new ArrayList<>(); - final ArrayList<int[]> outBigramProbabilityInfo = new ArrayList<>(); + final ArrayList<int[][]> outNgramPrevWordsArray = new ArrayList<>(); + final ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray = + new ArrayList<>(); + final ArrayList<int[]> outNgramTargets = new ArrayList<>(); + final ArrayList<int[]> outNgramProbabilityInfo = new ArrayList<>(); final ArrayList<int[]> outShortcutTargets = new ArrayList<>(); final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>(); getWordPropertyNative(mNativeDict, codePoints, isBeginningOfSentence, outCodePoints, - outFlags, outProbabilityInfo, outBigramTargets, outBigramProbabilityInfo, - outShortcutTargets, outShortcutProbabilities); + outFlags, outProbabilityInfo, outNgramPrevWordsArray, + outNgramPrevWordIsBeginningOfSentenceArray, outNgramTargets, + outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); return new WordProperty(codePoints, outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX], outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX], - outFlags[FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX], + outFlags[FORMAT_WORD_PROPERTY_HAS_NGRAMS_INDEX], outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outFlags[FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX], outProbabilityInfo, - outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, + outNgramPrevWordsArray, outNgramPrevWordIsBeginningOfSentenceArray, + outNgramTargets, outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); } @@ -506,7 +512,7 @@ public final class BinaryDictionary extends Dictionary { final boolean[] isBeginningOfSentenceArray = new boolean[ngramContext.getPrevWordCount()]; ngramContext.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray); final int[] wordCodePoints = StringUtils.toCodePointArray(word); - if (!updateCounterNative(mNativeDict, prevWordCodePointArrays, + if (!updateEntriesForWordWithNgramContextNative(mNativeDict, prevWordCodePointArrays, isBeginningOfSentenceArray, wordCodePoints, isValidWord, count, timestamp)) { return false; } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 43561ba4b..e66847b56 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; @@ -36,19 +37,19 @@ public abstract class Dictionary { // The following types do not actually come from real dictionary instances, so we create // corresponding instances. public static final String TYPE_USER_TYPED = "user_typed"; - public static final Dictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED); + public static final PhonyDictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED); public static final String TYPE_APPLICATION_DEFINED = "application_defined"; - public static final Dictionary DICTIONARY_APPLICATION_DEFINED = + public static final PhonyDictionary DICTIONARY_APPLICATION_DEFINED = new PhonyDictionary(TYPE_APPLICATION_DEFINED); public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such - public static final Dictionary DICTIONARY_HARDCODED = + public static final PhonyDictionary DICTIONARY_HARDCODED = new PhonyDictionary(TYPE_HARDCODED); // Spawned by resuming suggestions. Comes from a span that was in the TextView. public static final String TYPE_RESUMED = "resumed"; - public static final Dictionary DICTIONARY_RESUMED = + public static final PhonyDictionary DICTIONARY_RESUMED = new PhonyDictionary(TYPE_RESUMED); // The following types of dictionary have actual functional instances. We don't need final @@ -182,9 +183,10 @@ public abstract class Dictionary { * Not a true dictionary. A placeholder used to indicate suggestions that don't come from any * real dictionary. */ - private static class PhonyDictionary extends Dictionary { - // This class is not publicly instantiable. - private PhonyDictionary(final String type) { + @UsedForTesting + static class PhonyDictionary extends Dictionary { + @UsedForTesting + PhonyDictionary(final String type) { super(type, null); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 6a63bfda7..08035dfd6 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -257,6 +257,12 @@ public class DictionaryFacilitator { } public void switchMostProbableLanguage(final Locale locale) { + if (null == locale) { + // In many cases, there is no locale to a committed word. For example, a typed word + // that does not auto-correct has no locale. In this case we simply do not change + // the most probable language. + return; + } final DictionaryGroup newMostProbableDictionaryGroup = findDictionaryGroupWithLocale(mDictionaryGroups, locale); mMostProbableDictionaryGroup.mWeightForTypingInLocale = diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index d24f80a45..d58373ff6 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -64,9 +64,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; - private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000; - private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000; - /** * The maximum length of a word in this dictionary. */ @@ -225,10 +222,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString()); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); - attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, - String.valueOf(DEFAULT_MAX_UNIGRAM_COUNT)); - attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, - String.valueOf(DEFAULT_MAX_BIGRAM_COUNT)); return attributeMap; } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ec45462c4..91a4ec84c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -60,6 +60,8 @@ import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils; +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.Event; import com.android.inputmethod.event.HardwareEventDecoder; @@ -85,7 +87,6 @@ import com.android.inputmethod.latin.settings.SettingsActivity; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; -import com.android.inputmethod.latin.sync.BeanstalkManager; import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; @@ -109,6 +110,8 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + /** * Input method implementation for Qwerty'ish keyboard. */ @@ -154,6 +157,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: Move these {@link View}s to {@link KeyboardSwitcher}. private View mInputView; + private InsetsUpdater mInsetsUpdater; private SuggestionStripView mSuggestionStripView; private TextView mExtractEditText; @@ -206,7 +210,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private int mDelayInMillisecondsToUpdateSuggestions; private int mDelayInMillisecondsToUpdateShiftState; - public UIHandler(final LatinIME ownerInstance) { + public UIHandler(@Nonnull final LatinIME ownerInstance) { super(ownerInstance); } @@ -558,7 +562,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); mStatsUtilsManager.onCreate(this /* context */); - BeanstalkManager.getInstance(this /* context */).onCreate(); super.onCreate(); mHandler.onCreate(); @@ -567,6 +570,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: Resolve mutual dependencies of {@link #loadSettings()} and // {@link #resetDictionaryFacilitatorIfNecessary()}. loadSettings(); + mSubtypeSwitcher.onSubtypeChanged(mRichImm.getCurrentRawSubtype()); resetDictionaryFacilitatorIfNecessary(); // Register to receive ringer mode change and network state change. @@ -706,7 +710,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen unregisterReceiver(mDictionaryPackInstallReceiver); unregisterReceiver(mDictionaryDumpBroadcastReceiver); mStatsUtilsManager.onDestroy(); - BeanstalkManager.getInstance(this /* context */).onDestroy(); super.onDestroy(); } @@ -754,6 +757,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void setInputView(final View view) { super.setInputView(view); mInputView = view; + mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view); updateSoftInputWindowLayoutParameters(); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); if (hasSuggestionStripView()) { @@ -791,23 +795,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - onExtractTextViewPreDraw(); + // CursorAnchorInfo is used on L and later. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (isFullscreenMode() && mExtractEditText != null) { + mInputLogic.onUpdateCursorAnchorInfo( + CursorAnchorInfoUtils.extractFromTextView(mExtractEditText)); + } + } return true; } }; - private void onExtractTextViewPreDraw() { - // CursorAnchorInfo is available on L and later. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.L) { - return; - } - if (!isFullscreenMode() || mExtractEditText == null) { - return; - } - final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText); - mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); - } - @Override public void setCandidatesView(final View view) { // To ensure that CandidatesView will never be set. @@ -842,8 +840,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) { // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. - final RichInputMethodSubtype richSubtype = new RichInputMethodSubtype(subtype); - mSubtypeSwitcher.onSubtypeChanged(richSubtype); + mSubtypeSwitcher.onSubtypeChanged(subtype); mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype), mSettings.getCurrent()); loadKeyboard(); @@ -860,6 +857,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // also wouldn't be consuming gesture data. mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER; mRichImm.clearSubtypeCaches(); + mSubtypeSwitcher.refreshSubtypeInfo(); final KeyboardSwitcher switcher = mKeyboardSwitcher; switcher.updateKeyboardTheme(); final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView(); @@ -1078,7 +1076,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // with cursor movement when we have a hardware keyboard since we are not in charge. final SettingsValues settingsValues = mSettings.getCurrent(); if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) - && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) { + && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, + settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } @@ -1090,7 +1089,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isFullscreenMode()) { return; } - mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); + mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.wrap(info)); } /** @@ -1191,6 +1190,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // no visual element will be shown on the screen. outInsets.touchableInsets = inputHeight; outInsets.visibleTopInsets = inputHeight; + mInsetsUpdater.setInsets(outInsets); return; } final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes() @@ -1211,6 +1211,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } outInsets.contentTopInsets = visibleTopY; outInsets.visibleTopInsets = visibleTopY; + mInsetsUpdater.setInsets(outInsets); } public void startShowingInputView(final boolean needsToLoadKeyboard) { @@ -1627,7 +1628,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public void showAddToDictionaryHint(final String word) { + public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip) { if (!hasSuggestionStripView()) { return; } @@ -1637,7 +1638,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { wordToShow = word; } - mSuggestionStripView.showAddToDictionaryHint(wordToShow); + mSuggestionStripView.showAddToDictionaryHint(wordToShow, + isFromSuggestionStrip /* shouldShowWordToSave */); } // This will show either an empty suggestion strip (if prediction is enabled) or diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 62a258b20..a3f7bb4d6 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -860,9 +860,10 @@ public final class RichInputConnection implements PrivateCommandPerformer { * than it really is. */ public void tryFixLyingCursorPosition() { + mIC = mParent.getCurrentInputConnection(); final CharSequence textBeforeCursor = getTextBeforeCursor( Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); - final CharSequence selectedText = mIC.getSelectedText(0 /* flags */); + final CharSequence selectedText = null == mIC ? null : mIC.getSelectedText(0 /* flags */); if (null == textBeforeCursor || (!TextUtils.isEmpty(selectedText) && mExpectedSelEnd == mExpectedSelStart)) { // If textBeforeCursor is null, we have no idea what kind of text field we have or if diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 0d5ce7d6d..e6df35bea 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; @@ -29,6 +30,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -50,6 +52,7 @@ public class RichInputMethodManager { private static final RichInputMethodManager sInstance = new RichInputMethodManager(); + private Context mContext; private InputMethodManagerCompatWrapper mImmWrapper; private InputMethodInfoCache mInputMethodInfoCache; final HashMap<InputMethodInfo, List<InputMethodSubtype>> @@ -83,6 +86,7 @@ public class RichInputMethodManager { return; } mImmWrapper = new InputMethodManagerCompatWrapper(context); + mContext = context; mInputMethodInfoCache = new InputMethodInfoCache( mImmWrapper.mImm, context.getPackageName()); @@ -298,14 +302,14 @@ public class RichInputMethodManager { return INDEX_NOT_FOUND; } - public RichInputMethodSubtype getCurrentInputMethodSubtype( - final RichInputMethodSubtype defaultSubtype) { - final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype(); - if (currentSubtype == null) { - return defaultSubtype; - } - // TODO: Determine locales to use for multi-lingual use. - return new RichInputMethodSubtype(currentSubtype); + public InputMethodSubtype getCurrentRawSubtype() { + return mImmWrapper.mImm.getCurrentInputMethodSubtype(); + } + + public RichInputMethodSubtype createCurrentRichInputMethodSubtype( + final InputMethodSubtype rawSubtype) { + return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype, + mContext); } public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) { diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index c339e96fb..6fc549549 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -36,7 +36,6 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.HashSet; @@ -58,6 +57,7 @@ public final class SubtypeSwitcher { new LanguageOnSpacebarHelper(); private InputMethodInfo mShortcutInputMethodInfo; private InputMethodSubtype mShortcutSubtype; + private RichInputMethodSubtype mCurrentRichInputMethodSubtype; private RichInputMethodSubtype mNoLanguageSubtype; private RichInputMethodSubtype mEmojiSubtype; private boolean mIsNetworkConnected; @@ -117,10 +117,14 @@ public final class SubtypeSwitcher { final NetworkInfo info = connectivityManager.getActiveNetworkInfo(); mIsNetworkConnected = (info != null && info.isConnected()); - onSubtypeChanged(getCurrentSubtype()); + refreshSubtypeInfo(); updateParametersOnStartInputView(); } + public void refreshSubtypeInfo() { + onSubtypeChanged(mRichImm.getCurrentRawSubtype()); + } + /** * Update parameters which are changed outside LatinIME. This parameters affect UI so that they * should be updated every time onStartInputView is called. @@ -165,12 +169,14 @@ public final class SubtypeSwitcher { } // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. - public void onSubtypeChanged(final RichInputMethodSubtype newSubtype) { + public void onSubtypeChanged(final InputMethodSubtype newSubtype) { + final RichInputMethodSubtype richSubtype = + mRichImm.createCurrentRichInputMethodSubtype(newSubtype); if (DBG) { - Log.w(TAG, "onSubtypeChanged: " + newSubtype.getNameForLogging()); + Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging()); } - - final Locale[] newLocales = newSubtype.getLocales(); + mCurrentRichInputMethodSubtype = richSubtype; + final Locale[] newLocales = richSubtype.getLocales(); if (newLocales.length > 1) { // In multi-locales mode, the system language is never the same as the input language // because there is no single input language. @@ -181,7 +187,7 @@ public final class SubtypeSwitcher { final boolean sameLocale = systemLocale.equals(newLocale); final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage()); final boolean implicitlyEnabled = mRichImm - .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype.getRawSubtype()); + .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype); mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage( sameLocale || (sameLanguage && implicitlyEnabled)); } @@ -301,7 +307,7 @@ public final class SubtypeSwitcher { if (null != sForcedSubtypeForTesting) { return sForcedSubtypeForTesting; } - return mRichImm.getCurrentInputMethodSubtype(getNoLanguageSubtype()); + return mCurrentRichInputMethodSubtype; } public RichInputMethodSubtype getNoLanguageSubtype() { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index e181237a6..e6c138407 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -28,6 +28,7 @@ import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; import java.util.ArrayList; +import java.util.HashMap; import java.util.Locale; /** @@ -49,6 +50,16 @@ public final class Suggest { private static final boolean DBG = DebugFlags.DEBUG_ENABLED; private final DictionaryFacilitator mDictionaryFacilitator; + private static final int MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN = 12; + private static final HashMap<String, Integer> sLanguageToMaximumAutoCorrectionWithSpaceLength = + new HashMap<>(); + static { + // TODO: should we add Finnish here? + // TODO: This should not be hardcoded here but be written in the dictionary header + sLanguageToMaximumAutoCorrectionWithSpaceLength.put(Locale.GERMAN.getLanguage(), + MAXIMUM_AUTO_CORRECT_LENGTH_FOR_GERMAN); + } + private float mAutoCorrectionThreshold; public Suggest(final DictionaryFacilitator dictionaryFacilitator) { @@ -168,8 +179,18 @@ public final class Suggest { // TODO: we may want to have shortcut-only entries auto-correct in the future. hasAutoCorrection = false; } else { - hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( - suggestionResults.first(), consideredWord, mAutoCorrectionThreshold); + final SuggestedWordInfo firstSuggestion = suggestionResults.first(); + if (!AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( + firstSuggestion, consideredWord, mAutoCorrectionThreshold)) { + // Score is too low for autocorrect + hasAutoCorrection = false; + } else { + // We have a high score, so we need to check if this suggestion is in the correct + // form to allow auto-correcting to it in this language. For details of how this + // is determined, see #isAllowedByAutoCorrectionWithSpaceFilter. + // TODO: this should not have its own logic here but be handled by the dictionary. + hasAutoCorrection = isAllowedByAutoCorrectionWithSpaceFilter(firstSuggestion); + } } if (!TextUtils.isEmpty(typedWord)) { @@ -287,6 +308,41 @@ public final class Suggest { return suggestionsList; } + /** + * Computes whether this suggestion should be blocked or not in this language + * + * This function implements a filter that avoids auto-correcting to suggestions that contain + * spaces that are above a certain language-dependent character limit. In languages like German + * where it's possible to concatenate many words, it often happens our dictionary does not + * have the longer words. In this case, we offer a lot of unhelpful suggestions that contain + * one or several spaces. Ideally we should understand what the user wants and display useful + * suggestions by improving the dictionary and possibly having some specific logic. Until + * that's possible we should avoid displaying unhelpful suggestions. But it's hard to tell + * whether a suggestion is useful or not. So at least for the time being we block + * auto-correction when the suggestion is long and contains a space, which should avoid the + * worst damage. + * This function is implementing that filter. If the language enforces no such limit, then it + * always returns true. If the suggestion contains no space, it also returns true. Otherwise, + * it checks the length against the language-specific limit. + * + * @param info the suggestion info + * @return whether it's fine to auto-correct to this. + */ + private boolean isAllowedByAutoCorrectionWithSpaceFilter(final SuggestedWordInfo info) { + final Locale locale = info.mSourceDict.mLocale; + if (null == locale) { + return true; + } + final Integer maximumLengthForThisLanguage = + sLanguageToMaximumAutoCorrectionWithSpaceLength.get(locale.getLanguage()); + if (null == maximumLengthForThisLanguage) { + // This language does not enforce a maximum length to auto-correction + return true; + } + return info.mWord.length() <= maximumLengthForThisLanguage + || -1 == info.mWord.indexOf(Constants.CODE_SPACE); + } + /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo( final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) { diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 466576465..e5bf25d5f 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -243,7 +243,8 @@ public class SuggestedWords { return candidate.isEligibleForAutoCommit() ? candidate : null; } - public static final class SuggestedWordInfo { + // non-final for testability. + public static class SuggestedWordInfo { public static final int NOT_AN_INDEX = -1; public static final int NOT_A_CONFIDENCE = -1; public static final int MAX_SCORE = Integer.MAX_VALUE; @@ -413,28 +414,6 @@ public class SuggestedWords { return isPrediction(mInputStyle); } - // SuggestedWords is an immutable object, as much as possible. We must not just remove - // words from the member ArrayList as some other parties may expect the object to never change. - // This is only ever called by recorrection at the moment, hence the ForRecorrection moniker. - public SuggestedWords getSuggestedWordsExcludingTypedWordForRecorrection() { - final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>(); - String typedWord = null; - for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { - final SuggestedWordInfo info = mSuggestedWordInfoList.get(i); - if (!info.isKindOf(SuggestedWordInfo.KIND_TYPED)) { - newSuggestions.add(info); - } else { - assert(null == typedWord); - typedWord = info.mWord; - } - } - // We should never autocorrect, so we say the typed word is valid. Also, in this case, - // no auto-correction should take place hence willAutoCorrect = false. - return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord, - true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions, - SuggestedWords.INPUT_STYLE_RECORRECTION, NOT_A_SEQUENCE_NUMBER); - } - // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the // last word of all suggestions, separated by a space. This is necessary because when we commit // a multiple-word suggestion, the IME only retains the last word as the composing word, and diff --git a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java index 9445ce4c3..00bcecf52 100644 --- a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java +++ b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java @@ -26,7 +26,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.settings.LocalSettingsConstants; /** * {@link BroadcastReceiver} for {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION}. @@ -41,25 +41,14 @@ public class AccountsChangedReceiver extends BroadcastReceiver { return; } + // Ideally the account preference could live in a different preferences file + // that wasn't being backed up and restored, however the preference fragments + // currently only deal with the default shared preferences which is why + // separating this out into a different file is not trivial currently. final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - final String currentAccount = prefs.getString(Settings.PREF_ACCOUNT_NAME, null); - if (currentAccount != null) { - final String[] accounts = getAccountsForLogin(context); - boolean accountFound = false; - for (String account : accounts) { - if (TextUtils.equals(currentAccount, account)) { - accountFound = true; - break; - } - } - // The current account was not found in the list of accounts, remove it. - if (!accountFound) { - Log.i(TAG, "The current account was removed from the system: " + currentAccount); - prefs.edit() - .remove(Settings.PREF_ACCOUNT_NAME) - .apply(); - } - } + final String currentAccount = prefs.getString( + LocalSettingsConstants.PREF_ACCOUNT_NAME, null); + removeUnknownAccountFromPreference(prefs, getAccountsForLogin(context), currentAccount); } /** @@ -69,4 +58,24 @@ public class AccountsChangedReceiver extends BroadcastReceiver { protected String[] getAccountsForLogin(Context context) { return LoginAccountUtils.getAccountsForLogin(context); } + + /** + * Removes the currentAccount from preferences if it's not found + * in the list of current accounts. + */ + private static void removeUnknownAccountFromPreference(final SharedPreferences prefs, + final String[] accounts, final String currentAccount) { + if (currentAccount == null) { + return; + } + for (final String account : accounts) { + if (TextUtils.equals(currentAccount, account)) { + return; + } + } + Log.i(TAG, "The current account was removed from the system: " + currentAccount); + prefs.edit() + .remove(LocalSettingsConstants.PREF_ACCOUNT_NAME) + .apply(); + } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 07f2ed3db..5cc61db79 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -305,6 +305,7 @@ public final class InputLogic { currentKeyboardScriptId, handler); } + mDictionaryFacilitator.switchMostProbableLanguage(suggestionInfo.mSourceDict.mLocale); final Event event = Event.createSuggestionPickedEvent(suggestionInfo); final InputTransaction inputTransaction = new InputTransaction(settingsValues, event, SystemClock.uptimeMillis(), mSpaceState, keyboardShiftState); @@ -348,7 +349,8 @@ public final class InputLogic { inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); if (shouldShowAddToDictionaryHint) { - mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion); + mSuggestionStripViewAccessor.suggestAddingToDictionary(suggestion, + true /* isFromSuggestionStrip */); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE. @@ -369,10 +371,11 @@ public final class InputLogic { * @param oldSelEnd old selection end * @param newSelStart new selection start * @param newSelEnd new selection end + * @param settingsValues the current values of the settings. * @return whether the cursor has moved as a result of user interaction. */ public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd) { + final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) { if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) { return false; } @@ -397,8 +400,9 @@ public final class InputLogic { // should be true, but that is if the framework had taken that wrong cursor position // into account, which means we have to reset the entire composing state whenever there // is or was a selection regardless of whether it changed or not. - if (hasOrHadSelection || (selectionChangedOrSafeToReset - && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions() + || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -1484,6 +1488,11 @@ public final class InputLogic { if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); final String typedWord = range.mWord.toString(); + suggestions.add(new SuggestedWordInfo(typedWord, + SuggestedWords.MAX_SUGGESTIONS + 1, + SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); if (!isResumableWord(settingsValues, typedWord)) { mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); return; @@ -1516,30 +1525,14 @@ public final class InputLogic { mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug(); mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); - if (suggestions.size() <= 0) { + if (suggestions.size() <= 1) { // If there weren't any suggestion spans on this word, suggestions#size() will be 1 // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we // have no useful suggestions, so we will try to compute some for it instead. mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override - public void onGetSuggestedWords( - final SuggestedWords suggestedWordsIncludingTypedWord) { - final SuggestedWords suggestedWords; - if (suggestedWordsIncludingTypedWord.size() > 1) { - // We were able to compute new suggestions for this word. - // Remove the typed word, since we don't want to display it in this - // case. The #getSuggestedWordsExcludingTypedWordForRecorrection() - // method sets willAutoCorrect to false. - suggestedWords = suggestedWordsIncludingTypedWord - .getSuggestedWordsExcludingTypedWordForRecorrection(); - } else { - // No saved suggestions, and we were unable to compute any good one - // either. Rather than displaying an empty suggestion strip, we'll - // display the original word alone in the middle. - // Since there is only one word, willAutoCorrect is false. - suggestedWords = suggestedWordsIncludingTypedWord; - } + public void onGetSuggestedWords(final SuggestedWords suggestedWords) { mIsAutoCorrectionIndicatorOn = false; mLatinIME.mHandler.showSuggestionStrip(suggestedWords); }}); @@ -1683,7 +1676,8 @@ public final class InputLogic { mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd()); } - mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString); + mSuggestionStripViewAccessor.suggestAddingToDictionary(originallyTypedWordString, + false /* isFromSuggestionStrip */); } else { // We have a separator between the word and the cursor: we should show predictions. inputTransaction.setRequiresUpdateSuggestions(); @@ -2100,6 +2094,10 @@ public final class InputLogic { final boolean isBatchMode = mWordComposer.isBatchMode(); commitChosenWord(settingsValues, stringToCommit, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator); + if (null != autoCorrectionOrNull) { + mDictionaryFacilitator.switchMostProbableLanguage( + autoCorrectionOrNull.mSourceDict.mLocale); + } if (!typedWord.equals(stringToCommit)) { // This will make the correction flash for a short while as a visual clue // to the user that auto-correction happened. It has no other effect; in particular diff --git a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java index fa7c2c417..644818ba6 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java @@ -40,8 +40,9 @@ public final class DictionaryHeader { public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE"; public static final String FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY = "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID"; - public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT"; - public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT"; + public static final String MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_ENTRY_COUNT"; + public static final String MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_ENTRY_COUNT"; + public static final String MAX_TRIGRAM_COUNT_KEY = "MAX_TRIGRAM_ENTRY_COUNT"; public static final String ATTRIBUTE_VALUE_TRUE = "1"; public static final String CODE_POINT_TABLE_KEY = "codePointTable"; diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index a180d060e..1e6cadf03 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -26,6 +26,8 @@ import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; +import javax.annotation.Nullable; + /** * Utility class for a word with a probability. * @@ -49,7 +51,7 @@ public final class WordProperty implements Comparable<WordProperty> { @UsedForTesting public WordProperty(final String word, final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, + @Nullable final ArrayList<WeightedString> bigrams, final boolean isNotAWord, final boolean isBlacklistEntry) { mWord = word; mProbabilityInfo = probabilityInfo; @@ -85,7 +87,9 @@ public final class WordProperty implements Comparable<WordProperty> { public WordProperty(final int[] codePoints, final boolean isNotAWord, final boolean isBlacklisted, final boolean hasBigram, final boolean hasShortcuts, final boolean isBeginningOfSentence, final int[] probabilityInfo, - final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo, + final ArrayList<int[][]> ngramPrevWordsArray, + final ArrayList<boolean[]> outNgramPrevWordIsBeginningOfSentenceArray, + final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo, final ArrayList<int[]> shortcutTargets, final ArrayList<Integer> shortcutProbabilities) { mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); @@ -98,15 +102,15 @@ public final class WordProperty implements Comparable<WordProperty> { mHasShortcuts = hasShortcuts; mHasNgrams = hasBigram; - final int relatedNgramCount = bigramTargets.size(); + final int relatedNgramCount = ngramTargets.size(); final WordInfo currentWordInfo = mIsBeginningOfSentence ? WordInfo.BEGINNING_OF_SENTENCE : new WordInfo(mWord); final NgramContext ngramContext = new NgramContext(currentWordInfo); for (int i = 0; i < relatedNgramCount; i++) { final String ngramTargetString = - StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i)); + StringUtils.getStringFromNullTerminatedCodePointArray(ngramTargets.get(i)); final WeightedString ngramTarget = new WeightedString(ngramTargetString, - createProbabilityInfoFromArray(bigramProbabilityInfo.get(i))); + createProbabilityInfoFromArray(ngramProbabilityInfo.get(i))); // TODO: Support n-gram. ngrams.add(new NgramProperty(ngramTarget, ngramContext)); } @@ -180,7 +184,8 @@ public final class WordProperty implements Comparable<WordProperty> { && mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams; } - private <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) { + // TDOO: Have a utility method like java.util.Objects.equals. + private static <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) { if (null == a) { return null == b; } diff --git a/java/src/com/android/inputmethod/latin/network/AuthException.java b/java/src/com/android/inputmethod/latin/network/AuthException.java new file mode 100644 index 000000000..1bce4c156 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/network/AuthException.java @@ -0,0 +1,35 @@ +/* + * 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.network; + +/** + * Authentication exception. When this exception is thrown, the client may + * try to refresh the authentication token and try again. + */ +public class AuthException extends Exception { + public AuthException() { + super(); + } + + public AuthException(Throwable throwable) { + super(throwable); + } + + public AuthException(String detailMessage) { + super(detailMessage); + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java index 0d0cbe169..e2d24fd0a 100644 --- a/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java +++ b/java/src/com/android/inputmethod/latin/network/BlockingHttpClient.java @@ -16,7 +16,7 @@ package com.android.inputmethod.latin.network; -import com.android.inputmethod.annotations.UsedForTesting; +import android.util.Log; import java.io.BufferedOutputStream; import java.io.IOException; @@ -30,25 +30,17 @@ import javax.annotation.Nullable; /** * A client for executing HTTP requests synchronously. * This must never be called from the main thread. - * - * TODO: Remove @UsedForTesting after this is actually used. */ -@UsedForTesting public class BlockingHttpClient { + private static final boolean DEBUG = false; + private static final String TAG = BlockingHttpClient.class.getSimpleName(); + private final HttpURLConnection mConnection; /** * Interface that handles processing the response for a request. */ - public interface ResponseProcessor { - /** - * Called when the HTTP request fails with an error. - * - * @param httpStatusCode The status code of the HTTP response. - * @param message The HTTP response message, if any, or null. - */ - void onError(int httpStatusCode, @Nullable String message); - + public interface ResponseProcessor<T> { /** * Called when the HTTP request finishes successfully. * The {@link InputStream} is closed by the client after the method finishes, @@ -56,13 +48,9 @@ public class BlockingHttpClient { * * @param response An input stream that can be used to read the HTTP response. */ - void onSuccess(InputStream response); + T onSuccess(InputStream response) throws IOException; } - /** - * TODO: Remove @UsedForTesting after this is actually used. - */ - @UsedForTesting public BlockingHttpClient(HttpURLConnection connection) { mConnection = connection; } @@ -70,16 +58,19 @@ public class BlockingHttpClient { /** * Executes the request on the underlying {@link HttpURLConnection}. * - * TODO: Remove @UsedForTesting after this is actually used. - * * @param request The request payload, if any, or null. - * @param responeProcessor A processor for the HTTP response. + * @param responseProcessor A processor for the HTTP response. */ - @UsedForTesting - public void execute(@Nullable byte[] request, @Nonnull ResponseProcessor responseProcessor) - throws IOException { + public <T> T execute(@Nullable byte[] request, @Nonnull ResponseProcessor<T> responseProcessor) + throws IOException, AuthException, HttpException { + if (DEBUG) { + Log.d(TAG, "execute: " + mConnection.getURL()); + } try { if (request != null) { + if (DEBUG) { + Log.d(TAG, "request size: " + request.length); + } OutputStream out = new BufferedOutputStream(mConnection.getOutputStream()); out.write(request); out.flush(); @@ -88,9 +79,17 @@ public class BlockingHttpClient { final int responseCode = mConnection.getResponseCode(); if (responseCode != HttpURLConnection.HTTP_OK) { - responseProcessor.onError(responseCode, mConnection.getResponseMessage()); + Log.w(TAG, "Response error: " + responseCode + ", Message: " + + mConnection.getResponseMessage()); + if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) { + throw new AuthException(mConnection.getResponseMessage()); + } + throw new HttpException(responseCode); } else { - responseProcessor.onSuccess(mConnection.getInputStream()); + if (DEBUG) { + Log.d(TAG, "request executed successfully"); + } + return responseProcessor.onSuccess(mConnection.getInputStream()); } } finally { mConnection.disconnect(); diff --git a/java/src/com/android/inputmethod/latin/network/HttpException.java b/java/src/com/android/inputmethod/latin/network/HttpException.java new file mode 100644 index 000000000..b9d8b6372 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/network/HttpException.java @@ -0,0 +1,46 @@ +/* + * 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.network; + +import com.android.inputmethod.annotations.UsedForTesting; + +/** + * The HttpException exception represents a XML/HTTP fault with a HTTP status code. + */ +public class HttpException extends Exception { + + /** + * The HTTP status code. + */ + private final int mStatusCode; + + /** + * @param statusCode int HTTP status code. + */ + public HttpException(int statusCode) { + super("Response Code: " + statusCode); + mStatusCode = statusCode; + } + + /** + * @return the HTTP status code related to this exception. + */ + @UsedForTesting + public int getHttpStatusCode() { + return mStatusCode; + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java index 35b65be56..502f72f17 100644 --- a/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java +++ b/java/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilder.java @@ -37,6 +37,11 @@ public class HttpUrlConnectionBuilder { private static final int DEFAULT_TIMEOUT_MILLIS = 5 * 1000; /** + * Request header key for authentication. + */ + public static final String HTTP_HEADER_AUTHORIZATION = "Authorization"; + + /** * Request header key for cache control. */ public static final String KEY_CACHE_CONTROL = "Cache-Control"; @@ -78,7 +83,7 @@ public class HttpUrlConnectionBuilder { * Sets the URL that'll be used for the request. * This *must* be set before calling {@link #build()} * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpUrlConnectionBuilder setUrl(String url) throws MalformedURLException { @@ -92,7 +97,7 @@ public class HttpUrlConnectionBuilder { /** * Sets the connect timeout. Defaults to {@value #DEFAULT_TIMEOUT} milliseconds. * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpUrlConnectionBuilder setConnectTimeout(int timeoutMillis) { @@ -107,7 +112,7 @@ public class HttpUrlConnectionBuilder { /** * Sets the read timeout. Defaults to {@value #DEFAULT_TIMEOUT} milliseconds. * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpUrlConnectionBuilder setReadTimeout(int timeoutMillis) { @@ -122,7 +127,7 @@ public class HttpUrlConnectionBuilder { /** * Adds an entry to the request header. * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpUrlConnectionBuilder addHeader(String key, String value) { @@ -131,10 +136,21 @@ public class HttpUrlConnectionBuilder { } /** + * Sets an authentication token. + * + * TODO: Remove @UsedForTesting after this method is actually used. + */ + @UsedForTesting + public HttpUrlConnectionBuilder setAuthToken(String value) { + mHeaderMap.put(HTTP_HEADER_AUTHORIZATION, value); + return this; + } + + /** * Sets the request to be executed such that the input is not buffered. * This may be set when the request size is known beforehand. * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpUrlConnectionBuilder setFixedLengthForStreaming(int length) { @@ -145,7 +161,7 @@ public class HttpUrlConnectionBuilder { /** * Indicates if the request can use cached responses or not. * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpUrlConnectionBuilder setUseCache(boolean useCache) { @@ -161,7 +177,7 @@ public class HttpUrlConnectionBuilder { * @see #MODE_DOWNLOAD_ONLY * @see #MODE_BI_DIRECTIONAL * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used */ @UsedForTesting public HttpUrlConnectionBuilder setMode(int mode) { @@ -177,7 +193,7 @@ public class HttpUrlConnectionBuilder { /** * Builds the {@link HttpURLConnection} instance that can be used to execute the request. * - * TODO: Remove @UsedForTesting after this is actually used. + * TODO: Remove @UsedForTesting after this method is actually used. */ @UsedForTesting public HttpURLConnection build() throws IOException { @@ -210,4 +226,4 @@ public class HttpUrlConnectionBuilder { } return connection; } -} +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java index 32880038f..4bd15d037 100644 --- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java @@ -16,6 +16,9 @@ package com.android.inputmethod.latin.settings; +import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ACCOUNT_NAME; +import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ENABLE_CLOUD_SYNC; + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -32,8 +35,8 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.accounts.LoginAccountUtils; +import com.android.inputmethod.latin.accounts.AccountStateChangedListener; import com.android.inputmethod.latin.define.ProductionFlags; -import com.android.inputmethod.latin.sync.BeanstalkManager; import javax.annotation.Nullable; @@ -41,19 +44,17 @@ import javax.annotation.Nullable; * "Accounts & Privacy" settings sub screen. * * This settings sub screen handles the following preferences: - * <li> Account selection/management for IME - * <li> TODO: Sync preferences - * <li> TODO: Privacy preferences - * <li> Sync now + * <li> Account selection/management for IME </li> + * <li> Sync preferences </li> + * <li> Privacy preferences </li> */ public final class AccountsSettingsFragment extends SubScreenFragment { + private static final String PREF_SYNC_NOW = "pref_beanstalk"; + static final String PREF_ACCCOUNT_SWITCHER = "account_switcher"; - static final String PREF_SYNC_NOW = "pref_beanstalk"; - private final DialogInterface.OnClickListener mAccountSelectedListener = - new AccountSelectedListener(); - private final DialogInterface.OnClickListener mAccountSignedOutListener = - new AccountSignedOutListener(); + private final DialogInterface.OnClickListener mAccountChangedListener = + new AccountChangedListener(); private final Preference.OnPreferenceClickListener mSyncNowListener = new SyncNowListener(); @Override @@ -81,47 +82,56 @@ public final class AccountsSettingsFragment extends SubScreenFragment { removePreference(Settings.PREF_ENABLE_METRICS_LOGGING); } + if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) { + removePreference(PREF_ACCCOUNT_SWITCHER); + removePreference(PREF_ENABLE_CLOUD_SYNC); + removePreference(PREF_SYNC_NOW); + } if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { + removePreference(PREF_ENABLE_CLOUD_SYNC); removePreference(PREF_SYNC_NOW); } else { final Preference syncNowPreference = findPreference(PREF_SYNC_NOW); - if (syncNowPreference != null) { - syncNowPreference.setOnPreferenceClickListener(mSyncNowListener); - } + syncNowPreference.setOnPreferenceClickListener(mSyncNowListener); } } @Override public void onResume() { super.onResume(); - refreshUi(); + refreshAccountAndDependentPreferences(getSignedInAccountName()); } @Override public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { - // TODO: Look at the preference that changed before refreshing the view. - refreshUi(); - } - - private void refreshUi() { - refreshAccountSelection(); - refreshSyncNow(); + if (TextUtils.equals(key, PREF_ACCOUNT_NAME)) { + refreshAccountAndDependentPreferences( + prefs.getString(PREF_ACCOUNT_NAME, null)); + } else if (TextUtils.equals(key, PREF_ENABLE_CLOUD_SYNC)) { + final boolean syncEnabled = prefs.getBoolean(PREF_ENABLE_CLOUD_SYNC, false); + AccountStateChangedListener.onSyncPreferenceChanged( + getSignedInAccountName(), syncEnabled); + } } - private void refreshAccountSelection() { + private void refreshAccountAndDependentPreferences(@Nullable final String currentAccount) { if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) { return; } - final String currentAccount = getCurrentlySelectedAccount(); final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER); if (currentAccount == null) { // No account is currently selected. accountSwitcher.setSummary(getString(R.string.no_accounts_selected)); + // Disable the sync preference UI. + disableSyncPreference(); } else { // Set the currently selected account. accountSwitcher.setSummary(getString(R.string.account_selected, currentAccount)); + // Enable the sync preference UI. + enableSyncPreference(); } + // Set up onClick listener for the account picker preference. final Context context = getActivity(); final String[] accountsForLogin = LoginAccountUtils.getAccountsForLogin(context); accountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() { @@ -129,45 +139,50 @@ public final class AccountsSettingsFragment extends SubScreenFragment { public boolean onPreferenceClick(Preference preference) { if (accountsForLogin.length == 0) { // TODO: Handle account addition. - Toast.makeText(getActivity(), - getString(R.string.account_select_cancel), Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), getString(R.string.account_select_cancel), + Toast.LENGTH_SHORT).show(); } else { createAccountPicker(accountsForLogin, currentAccount).show(); } return true; } }); - - // TODO: Depending on the account selection, enable/disable preferences that - // depend on an account. } /** - * Refreshes the "Sync Now" feature + * Enables the Sync preference UI and updates its summary. */ - private void refreshSyncNow() { + private void enableSyncPreference() { if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { return; } - final Preference syncNowPreference = findPreference(PREF_SYNC_NOW); - if (syncNowPreference == null) { + final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC); + syncPreference.setEnabled(true); + syncPreference.setSummary(R.string.cloud_sync_summary); + } + + /** + * Disables the Sync preference UI and updates its summary to indicate + * the fact that an account needs to be selected for sync. + */ + private void disableSyncPreference() { + if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { return; } - final String currentAccount = getCurrentlySelectedAccount(); - if (currentAccount == null) { - syncNowPreference.setEnabled(false); - syncNowPreference.setSummary(R.string.sync_now_summary); - } else { - syncNowPreference.setEnabled(true); - syncNowPreference.setSummary(R.string.sync_now_summary_disabled_signed_out); - } + final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC); + syncPreference.setEnabled(false); + syncPreference.setSummary(R.string.cloud_sync_summary_disabled_signed_out); } @Nullable - private String getCurrentlySelectedAccount() { - return getSharedPreferences().getString(Settings.PREF_ACCOUNT_NAME, null); + String getSignedInAccountName() { + return getSharedPreferences().getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null); + } + + boolean isSyncEnabled() { + return getSharedPreferences().getBoolean(PREF_ENABLE_CLOUD_SYNC, false); } /** @@ -200,51 +215,51 @@ public final class AccountsSettingsFragment extends SubScreenFragment { final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) .setTitle(R.string.account_select_title) .setSingleChoiceItems(accounts, index, null) - .setPositiveButton(R.string.account_select_ok, mAccountSelectedListener) + .setPositiveButton(R.string.account_select_ok, mAccountChangedListener) .setNegativeButton(R.string.account_select_cancel, null); if (isSignedIn) { - builder.setNeutralButton(R.string.account_select_sign_out, mAccountSignedOutListener); + builder.setNeutralButton(R.string.account_select_sign_out, mAccountChangedListener); } return builder.create(); } /** - * Listener for an account being selected from the picker. - * Persists the account to shared preferences. - */ - class AccountSelectedListener implements DialogInterface.OnClickListener { - @Override - public void onClick(DialogInterface dialog, int which) { - final ListView lv = ((AlertDialog)dialog).getListView(); - final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition()); - getSharedPreferences() - .edit() - .putString(Settings.PREF_ACCOUNT_NAME, (String) selectedItem) - .apply(); - } - } - - /** - * Listener for sign-out being initiated from from the picker. - * Removed the account from shared preferences. + * Listener for a account selection changes from the picker. + * Persists/removes the account to/from shared preferences and sets up sync if required. */ - class AccountSignedOutListener implements DialogInterface.OnClickListener { + class AccountChangedListener implements DialogInterface.OnClickListener { @Override public void onClick(DialogInterface dialog, int which) { - getSharedPreferences() - .edit() - .remove(Settings.PREF_ACCOUNT_NAME) - .apply(); + final String oldAccount = getSignedInAccountName(); + switch (which) { + case DialogInterface.BUTTON_POSITIVE: // Signed in + final ListView lv = ((AlertDialog)dialog).getListView(); + final String newAccount = + (String) lv.getItemAtPosition(lv.getCheckedItemPosition()); + getSharedPreferences() + .edit() + .putString(PREF_ACCOUNT_NAME, newAccount) + .apply(); + AccountStateChangedListener.onAccountSignedIn(oldAccount, newAccount); + break; + case DialogInterface.BUTTON_NEUTRAL: // Signed out + AccountStateChangedListener.onAccountSignedOut(oldAccount); + getSharedPreferences() + .edit() + .remove(PREF_ACCOUNT_NAME) + .apply(); + break; + } } } /** - * Listener that initates the process of sync in the background. + * Listener that initiates the process of sync in the background. */ class SyncNowListener implements Preference.OnPreferenceClickListener { @Override public boolean onPreferenceClick(final Preference preference) { - BeanstalkManager.getInstance(getActivity() /* context */).requestSync(); + AccountStateChangedListener.forceSync(getSignedInAccountName()); return true; } } diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java index 3303ab093..d2c9dbbe9 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java @@ -113,6 +113,7 @@ public final class AdvancedSettingsFragment extends SubScreenFragment { setupKeypressVibrationDurationSettings(); setupKeypressSoundVolumeSettings(); + setupKeyLongpressTimeoutSettings(); refreshEnablingsOfKeypressSoundAndVibrationSettings(); } @@ -249,4 +250,43 @@ public final class AdvancedSettingsFragment extends SubScreenFragment { } }); } + + private void setupKeyLongpressTimeoutSettings() { + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( + Settings.PREF_KEY_LONGPRESS_TIMEOUT); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putInt(key, value).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return Settings.readKeyLongpressTimeout(prefs, res); + } + + @Override + public int readDefaultValue(final String key) { + return Settings.readDefaultKeyLongpressTimeout(res); + } + + @Override + public String getValueText(final int value) { + return res.getString(R.string.abbreviation_unit_milliseconds, value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java new file mode 100644 index 000000000..c07b60a0b --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java @@ -0,0 +1,358 @@ +/* + * 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.settings; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.DialogPreference; +import android.preference.Preference; +import android.util.Log; +import android.view.View; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; + +import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.TreeSet; + +final class CustomInputStylePreference extends DialogPreference + implements DialogInterface.OnCancelListener { + private static final boolean DEBUG_SUBTYPE_ID = false; + + interface Listener { + public void onRemoveCustomInputStyle(CustomInputStylePreference stylePref); + public void onSaveCustomInputStyle(CustomInputStylePreference stylePref); + public void onAddCustomInputStyle(CustomInputStylePreference stylePref); + public SubtypeLocaleAdapter getSubtypeLocaleAdapter(); + public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter(); + } + + private static final String KEY_PREFIX = "subtype_pref_"; + private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new"; + + private InputMethodSubtype mSubtype; + private InputMethodSubtype mPreviousSubtype; + + private final Listener mProxy; + private Spinner mSubtypeLocaleSpinner; + private Spinner mKeyboardLayoutSetSpinner; + + public static CustomInputStylePreference newIncompleteSubtypePreference( + final Context context, final Listener proxy) { + return new CustomInputStylePreference(context, null, proxy); + } + + public CustomInputStylePreference(final Context context, final InputMethodSubtype subtype, + final Listener proxy) { + super(context, null); + setDialogLayoutResource(R.layout.additional_subtype_dialog); + setPersistent(false); + mProxy = proxy; + setSubtype(subtype); + } + + public void show() { + showDialog(null); + } + + public final boolean isIncomplete() { + return mSubtype == null; + } + + public InputMethodSubtype getSubtype() { + return mSubtype; + } + + public void setSubtype(final InputMethodSubtype subtype) { + mPreviousSubtype = mSubtype; + mSubtype = subtype; + if (isIncomplete()) { + setTitle(null); + setDialogTitle(R.string.add_style); + setKey(KEY_NEW_SUBTYPE); + } else { + final String displayName = + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); + setTitle(displayName); + setDialogTitle(displayName); + setKey(KEY_PREFIX + subtype.getLocale() + "_" + + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype)); + } + } + + public void revert() { + setSubtype(mPreviousSubtype); + } + + public boolean hasBeenModified() { + return mSubtype != null && !mSubtype.equals(mPreviousSubtype); + } + + @Override + protected View onCreateDialogView() { + final View v = super.onCreateDialogView(); + mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner); + mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); + mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); + mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); + // All keyboard layout names are in the Latin script and thus left to right. That means + // the view would align them to the left even if the system locale is RTL, but that + // would look strange. To fix this, we align them to the view's start, which will be + // natural for any direction. + ViewCompatUtils.setTextAlignment( + mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); + return v; + } + + @Override + protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) { + builder.setCancelable(true).setOnCancelListener(this); + if (isIncomplete()) { + builder.setPositiveButton(R.string.add, this) + .setNegativeButton(android.R.string.cancel, this); + } else { + builder.setPositiveButton(R.string.save, this) + .setNeutralButton(android.R.string.cancel, this) + .setNegativeButton(R.string.remove, this); + final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype); + final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype); + setSpinnerPosition(mSubtypeLocaleSpinner, localeItem); + setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem); + } + } + + private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) { + final SpinnerAdapter adapter = spinner.getAdapter(); + final int count = adapter.getCount(); + for (int i = 0; i < count; i++) { + final Object item = spinner.getItemAtPosition(i); + if (item.equals(itemToSelect)) { + spinner.setSelection(i); + return; + } + } + } + + @Override + public void onCancel(final DialogInterface dialog) { + if (isIncomplete()) { + mProxy.onRemoveCustomInputStyle(this); + } + } + + @Override + public void onClick(final DialogInterface dialog, final int which) { + super.onClick(dialog, which); + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + final boolean isEditing = !isIncomplete(); + final SubtypeLocaleItem locale = + (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem(); + final KeyboardLayoutSetItem layout = + (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); + final InputMethodSubtype subtype = + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + locale.mLocaleString, layout.mLayoutName); + setSubtype(subtype); + notifyChanged(); + if (isEditing) { + mProxy.onSaveCustomInputStyle(this); + } else { + mProxy.onAddCustomInputStyle(this); + } + break; + case DialogInterface.BUTTON_NEUTRAL: + // Nothing to do + break; + case DialogInterface.BUTTON_NEGATIVE: + mProxy.onRemoveCustomInputStyle(this); + break; + } + } + + private static int getSpinnerPosition(final Spinner spinner) { + if (spinner == null) return -1; + return spinner.getSelectedItemPosition(); + } + + private static void setSpinnerPosition(final Spinner spinner, final int position) { + if (spinner == null || position < 0) return; + spinner.setSelection(position); + } + + @Override + protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + final Dialog dialog = getDialog(); + if (dialog == null || !dialog.isShowing()) { + return superState; + } + + final SavedState myState = new SavedState(superState); + myState.mSubtype = mSubtype; + myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner); + myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner); + return myState; + } + + @Override + protected void onRestoreInstanceState(final Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + + final SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos); + setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos); + setSubtype(myState.mSubtype); + } + + static final class SavedState extends Preference.BaseSavedState { + InputMethodSubtype mSubtype; + int mSubtypeLocaleSelectedPos; + int mKeyboardLayoutSetSelectedPos; + + public SavedState(final Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mSubtypeLocaleSelectedPos); + dest.writeInt(mKeyboardLayoutSetSelectedPos); + dest.writeParcelable(mSubtype, 0); + } + + public SavedState(final Parcel source) { + super(source); + mSubtypeLocaleSelectedPos = source.readInt(); + mKeyboardLayoutSetSelectedPos = source.readInt(); + mSubtype = (InputMethodSubtype)source.readParcelable(null); + } + + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + @Override + public SavedState createFromParcel(final Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(final int size) { + return new SavedState[size]; + } + }; + } + + static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> { + public final String mLocaleString; + private final String mDisplayName; + + public SubtypeLocaleItem(final InputMethodSubtype subtype) { + mLocaleString = subtype.getLocale(); + mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale( + mLocaleString); + } + + // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} + // to get display name. + @Override + public String toString() { + return mDisplayName; + } + + @Override + public int compareTo(final SubtypeLocaleItem o) { + return mLocaleString.compareTo(o.mLocaleString); + } + } + + static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> { + private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName(); + + public SubtypeLocaleAdapter(final Context context) { + super(context, android.R.layout.simple_spinner_item); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + final TreeSet<SubtypeLocaleItem> items = new TreeSet<>(); + final InputMethodInfo imi = RichInputMethodManager.getInstance() + .getInputMethodInfoOfThisIme(); + final int count = imi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype subtype = imi.getSubtypeAt(i); + if (DEBUG_SUBTYPE_ID) { + Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s", + subtype.getLocale(), subtype.hashCode(), subtype.hashCode(), + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype))); + } + if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) { + items.add(new SubtypeLocaleItem(subtype)); + } + } + // TODO: Should filter out already existing combinations of locale and layout. + addAll(items); + } + } + + static final class KeyboardLayoutSetItem { + public final String mLayoutName; + private final String mDisplayName; + + public KeyboardLayoutSetItem(final InputMethodSubtype subtype) { + mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); + mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); + } + + // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} + // to get display name. + @Override + public String toString() { + return mDisplayName; + } + } + + static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> { + public KeyboardLayoutSetAdapter(final Context context) { + super(context, android.R.layout.simple_spinner_item); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + // TODO: Should filter out already existing combinations of locale and layout. + for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) { + // This is a dummy subtype with NO_LANGUAGE, only for display. + final InputMethodSubtype subtype = + AdditionalSubtypeUtils.createDummyAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, layout); + add(new KeyboardLayoutSetItem(subtype)); + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index c633fc167..46fcc7106 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -17,16 +17,12 @@ package com.android.inputmethod.latin.settings; import android.app.AlertDialog; -import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.preference.DialogPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; @@ -39,15 +35,9 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; -import android.widget.ArrayAdapter; -import android.widget.Spinner; -import android.widget.SpinnerAdapter; import android.widget.Toast; -import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; -import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; @@ -56,19 +46,18 @@ import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; -import java.util.TreeSet; -public final class CustomInputStyleSettingsFragment extends PreferenceFragment { +public final class CustomInputStyleSettingsFragment extends PreferenceFragment + implements CustomInputStylePreference.Listener { private static final String TAG = CustomInputStyleSettingsFragment.class.getSimpleName(); - private static final boolean DEBUG_SUBTYPE_ID = false; // Note: We would like to turn this debug flag true in order to see what input styles are // defined in a bug-report. private static final boolean DEBUG_CUSTOM_INPUT_STYLES = true; private RichInputMethodManager mRichImm; private SharedPreferences mPrefs; - private SubtypeLocaleAdapter mSubtypeLocaleAdapter; - private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; + private CustomInputStylePreference.SubtypeLocaleAdapter mSubtypeLocaleAdapter; + private CustomInputStylePreference.KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; private boolean mIsAddingNewSubtype; private AlertDialog mSubtypeEnablerNotificationDialog; @@ -79,320 +68,6 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { "is_subtype_enabler_notification_dialog_open"; private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler"; - static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> { - public final String mLocaleString; - private final String mDisplayName; - - public SubtypeLocaleItem(final InputMethodSubtype subtype) { - mLocaleString = subtype.getLocale(); - mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale( - mLocaleString); - } - - // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} - // to get display name. - @Override - public String toString() { - return mDisplayName; - } - - @Override - public int compareTo(final SubtypeLocaleItem o) { - return mLocaleString.compareTo(o.mLocaleString); - } - } - - static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> { - private static final String TAG_SUBTYPE = SubtypeLocaleAdapter.class.getSimpleName(); - - public SubtypeLocaleAdapter(final Context context) { - super(context, android.R.layout.simple_spinner_item); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - final TreeSet<SubtypeLocaleItem> items = new TreeSet<>(); - final InputMethodInfo imi = RichInputMethodManager.getInstance() - .getInputMethodInfoOfThisIme(); - final int count = imi.getSubtypeCount(); - for (int i = 0; i < count; i++) { - final InputMethodSubtype subtype = imi.getSubtypeAt(i); - if (DEBUG_SUBTYPE_ID) { - Log.d(TAG_SUBTYPE, String.format("%-6s 0x%08x %11d %s", - subtype.getLocale(), subtype.hashCode(), subtype.hashCode(), - SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype))); - } - if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) { - items.add(new SubtypeLocaleItem(subtype)); - } - } - // TODO: Should filter out already existing combinations of locale and layout. - addAll(items); - } - } - - static final class KeyboardLayoutSetItem { - public final String mLayoutName; - private final String mDisplayName; - - public KeyboardLayoutSetItem(final InputMethodSubtype subtype) { - mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); - mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype); - } - - // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()} - // to get display name. - @Override - public String toString() { - return mDisplayName; - } - } - - static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> { - public KeyboardLayoutSetAdapter(final Context context) { - super(context, android.R.layout.simple_spinner_item); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - // TODO: Should filter out already existing combinations of locale and layout. - for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) { - // This is a dummy subtype with NO_LANGUAGE, only for display. - final InputMethodSubtype subtype = - AdditionalSubtypeUtils.createDummyAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, layout); - add(new KeyboardLayoutSetItem(subtype)); - } - } - } - - private interface SubtypeDialogProxy { - public void onRemovePressed(SubtypePreference subtypePref); - public void onSavePressed(SubtypePreference subtypePref); - public void onAddPressed(SubtypePreference subtypePref); - public SubtypeLocaleAdapter getSubtypeLocaleAdapter(); - public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter(); - } - - static final class SubtypePreference extends DialogPreference - implements DialogInterface.OnCancelListener { - private static final String KEY_PREFIX = "subtype_pref_"; - private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new"; - - private InputMethodSubtype mSubtype; - private InputMethodSubtype mPreviousSubtype; - - private final SubtypeDialogProxy mProxy; - private Spinner mSubtypeLocaleSpinner; - private Spinner mKeyboardLayoutSetSpinner; - - public static SubtypePreference newIncompleteSubtypePreference(final Context context, - final SubtypeDialogProxy proxy) { - return new SubtypePreference(context, null, proxy); - } - - public SubtypePreference(final Context context, final InputMethodSubtype subtype, - final SubtypeDialogProxy proxy) { - super(context, null); - setDialogLayoutResource(R.layout.additional_subtype_dialog); - setPersistent(false); - mProxy = proxy; - setSubtype(subtype); - } - - public void show() { - showDialog(null); - } - - public final boolean isIncomplete() { - return mSubtype == null; - } - - public InputMethodSubtype getSubtype() { - return mSubtype; - } - - public void setSubtype(final InputMethodSubtype subtype) { - mPreviousSubtype = mSubtype; - mSubtype = subtype; - if (isIncomplete()) { - setTitle(null); - setDialogTitle(R.string.add_style); - setKey(KEY_NEW_SUBTYPE); - } else { - final String displayName = - SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); - setTitle(displayName); - setDialogTitle(displayName); - setKey(KEY_PREFIX + subtype.getLocale() + "_" - + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype)); - } - } - - public void revert() { - setSubtype(mPreviousSubtype); - } - - public boolean hasBeenModified() { - return mSubtype != null && !mSubtype.equals(mPreviousSubtype); - } - - @Override - protected View onCreateDialogView() { - final View v = super.onCreateDialogView(); - mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner); - mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); - mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); - mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); - // All keyboard layout names are in the Latin script and thus left to right. That means - // the view would align them to the left even if the system locale is RTL, but that - // would look strange. To fix this, we align them to the view's start, which will be - // natural for any direction. - ViewCompatUtils.setTextAlignment( - mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); - return v; - } - - @Override - protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) { - builder.setCancelable(true).setOnCancelListener(this); - if (isIncomplete()) { - builder.setPositiveButton(R.string.add, this) - .setNegativeButton(android.R.string.cancel, this); - } else { - builder.setPositiveButton(R.string.save, this) - .setNeutralButton(android.R.string.cancel, this) - .setNegativeButton(R.string.remove, this); - final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype); - final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype); - setSpinnerPosition(mSubtypeLocaleSpinner, localeItem); - setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem); - } - } - - private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) { - final SpinnerAdapter adapter = spinner.getAdapter(); - final int count = adapter.getCount(); - for (int i = 0; i < count; i++) { - final Object item = spinner.getItemAtPosition(i); - if (item.equals(itemToSelect)) { - spinner.setSelection(i); - return; - } - } - } - - @Override - public void onCancel(final DialogInterface dialog) { - if (isIncomplete()) { - mProxy.onRemovePressed(this); - } - } - - @Override - public void onClick(final DialogInterface dialog, final int which) { - super.onClick(dialog, which); - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - final boolean isEditing = !isIncomplete(); - final SubtypeLocaleItem locale = - (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem(); - final KeyboardLayoutSetItem layout = - (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); - final InputMethodSubtype subtype = - AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - locale.mLocaleString, layout.mLayoutName); - setSubtype(subtype); - notifyChanged(); - if (isEditing) { - mProxy.onSavePressed(this); - } else { - mProxy.onAddPressed(this); - } - break; - case DialogInterface.BUTTON_NEUTRAL: - // Nothing to do - break; - case DialogInterface.BUTTON_NEGATIVE: - mProxy.onRemovePressed(this); - break; - } - } - - private static int getSpinnerPosition(final Spinner spinner) { - if (spinner == null) return -1; - return spinner.getSelectedItemPosition(); - } - - private static void setSpinnerPosition(final Spinner spinner, final int position) { - if (spinner == null || position < 0) return; - spinner.setSelection(position); - } - - @Override - protected Parcelable onSaveInstanceState() { - final Parcelable superState = super.onSaveInstanceState(); - final Dialog dialog = getDialog(); - if (dialog == null || !dialog.isShowing()) { - return superState; - } - - final SavedState myState = new SavedState(superState); - myState.mSubtype = mSubtype; - myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner); - myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner); - return myState; - } - - @Override - protected void onRestoreInstanceState(final Parcelable state) { - if (!(state instanceof SavedState)) { - super.onRestoreInstanceState(state); - return; - } - - final SavedState myState = (SavedState) state; - super.onRestoreInstanceState(myState.getSuperState()); - setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos); - setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos); - setSubtype(myState.mSubtype); - } - - static final class SavedState extends Preference.BaseSavedState { - InputMethodSubtype mSubtype; - int mSubtypeLocaleSelectedPos; - int mKeyboardLayoutSetSelectedPos; - - public SavedState(final Parcelable superState) { - super(superState); - } - - @Override - public void writeToParcel(final Parcel dest, final int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(mSubtypeLocaleSelectedPos); - dest.writeInt(mKeyboardLayoutSetSelectedPos); - dest.writeParcelable(mSubtype, 0); - } - - public SavedState(final Parcel source) { - super(source); - mSubtypeLocaleSelectedPos = source.readInt(); - mKeyboardLayoutSetSelectedPos = source.readInt(); - mSubtype = (InputMethodSubtype)source.readParcelable(null); - } - - public static final Parcelable.Creator<SavedState> CREATOR = - new Parcelable.Creator<SavedState>() { - @Override - public SavedState createFromParcel(final Parcel source) { - return new SavedState(source); - } - - @Override - public SavedState[] newArray(final int size) { - return new SavedState[size]; - } - }; - } - } - public CustomInputStyleSettingsFragment() { // Empty constructor for fragment generation. } @@ -440,8 +115,9 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { @Override public void onActivityCreated(final Bundle savedInstanceState) { final Context context = getActivity(); - mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context); - mKeyboardLayoutSetAdapter = new KeyboardLayoutSetAdapter(context); + mSubtypeLocaleAdapter = new CustomInputStylePreference.SubtypeLocaleAdapter(context); + mKeyboardLayoutSetAdapter = + new CustomInputStylePreference.KeyboardLayoutSetAdapter(context); final String prefSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources()); @@ -454,7 +130,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { && savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE); if (mIsAddingNewSubtype) { getPreferenceScreen().addPreference( - SubtypePreference.newIncompleteSubtypePreference(context, mSubtypeProxy)); + CustomInputStylePreference.newIncompleteSubtypePreference(context, this)); } super.onActivityCreated(savedInstanceState); @@ -482,62 +158,60 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } } - private final SubtypeDialogProxy mSubtypeProxy = new SubtypeDialogProxy() { - @Override - public void onRemovePressed(final SubtypePreference subtypePref) { - mIsAddingNewSubtype = false; - final PreferenceGroup group = getPreferenceScreen(); - group.removePreference(subtypePref); + @Override + public void onRemoveCustomInputStyle(final CustomInputStylePreference stylePref) { + mIsAddingNewSubtype = false; + final PreferenceGroup group = getPreferenceScreen(); + group.removePreference(stylePref); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); + } + + @Override + public void onSaveCustomInputStyle(final CustomInputStylePreference stylePref) { + final InputMethodSubtype subtype = stylePref.getSubtype(); + if (!stylePref.hasBeenModified()) { + return; + } + if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); + return; } - @Override - public void onSavePressed(final SubtypePreference subtypePref) { - final InputMethodSubtype subtype = subtypePref.getSubtype(); - if (!subtypePref.hasBeenModified()) { - return; - } - if (findDuplicatedSubtype(subtype) == null) { - mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); - return; - } + // Saved subtype is duplicated. + final PreferenceGroup group = getPreferenceScreen(); + group.removePreference(stylePref); + stylePref.revert(); + group.addPreference(stylePref); + showSubtypeAlreadyExistsToast(subtype); + } - // Saved subtype is duplicated. - final PreferenceGroup group = getPreferenceScreen(); - group.removePreference(subtypePref); - subtypePref.revert(); - group.addPreference(subtypePref); - showSubtypeAlreadyExistsToast(subtype); + @Override + public void onAddCustomInputStyle(final CustomInputStylePreference stylePref) { + mIsAddingNewSubtype = false; + final InputMethodSubtype subtype = stylePref.getSubtype(); + if (findDuplicatedSubtype(subtype) == null) { + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); + mSubtypePreferenceKeyForSubtypeEnabler = stylePref.getKey(); + mSubtypeEnablerNotificationDialog = createDialog(); + mSubtypeEnablerNotificationDialog.show(); + return; } - @Override - public void onAddPressed(final SubtypePreference subtypePref) { - mIsAddingNewSubtype = false; - final InputMethodSubtype subtype = subtypePref.getSubtype(); - if (findDuplicatedSubtype(subtype) == null) { - mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); - mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); - mSubtypeEnablerNotificationDialog = createDialog(); - mSubtypeEnablerNotificationDialog.show(); - return; - } - - // Newly added subtype is duplicated. - final PreferenceGroup group = getPreferenceScreen(); - group.removePreference(subtypePref); - showSubtypeAlreadyExistsToast(subtype); - } + // Newly added subtype is duplicated. + final PreferenceGroup group = getPreferenceScreen(); + group.removePreference(stylePref); + showSubtypeAlreadyExistsToast(subtype); + } - @Override - public SubtypeLocaleAdapter getSubtypeLocaleAdapter() { - return mSubtypeLocaleAdapter; - } + @Override + public CustomInputStylePreference.SubtypeLocaleAdapter getSubtypeLocaleAdapter() { + return mSubtypeLocaleAdapter; + } - @Override - public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() { - return mKeyboardLayoutSetAdapter; - } - }; + @Override + public CustomInputStylePreference.KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() { + return mKeyboardLayoutSetAdapter; + } private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) { final Context context = getActivity(); @@ -555,6 +229,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } private AlertDialog createDialog() { + final String imeId = mRichImm.getInputMethodIdOfThisIme(); final AlertDialog.Builder builder = new AlertDialog.Builder( DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) @@ -564,7 +239,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { @Override public void onClick(DialogInterface dialog, int which) { final Intent intent = IntentUtils.getInputLanguageSelectionIntent( - mRichImm.getInputMethodIdOfThisIme(), + imeId, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -584,8 +259,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { final InputMethodSubtype[] subtypesArray = AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes); for (final InputMethodSubtype subtype : subtypesArray) { - final SubtypePreference pref = new SubtypePreference( - context, subtype, mSubtypeProxy); + final CustomInputStylePreference pref = + new CustomInputStylePreference(context, subtype, this); group.addPreference(pref); } } @@ -596,8 +271,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { final int count = group.getPreferenceCount(); for (int i = 0; i < count; i++) { final Preference pref = group.getPreference(i); - if (pref instanceof SubtypePreference) { - final SubtypePreference subtypePref = (SubtypePreference)pref; + if (pref instanceof CustomInputStylePreference) { + final CustomInputStylePreference subtypePref = (CustomInputStylePreference)pref; // We should not save newly adding subtype to preference because it is incomplete. if (subtypePref.isIncomplete()) continue; subtypes.add(subtypePref.getSubtype()); @@ -631,8 +306,8 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { public boolean onOptionsItemSelected(final MenuItem item) { final int itemId = item.getItemId(); if (itemId == R.id.action_add_style) { - final SubtypePreference newSubtype = - SubtypePreference.newIncompleteSubtypePreference(getActivity(), mSubtypeProxy); + final CustomInputStylePreference newSubtype = + CustomInputStylePreference.newIncompleteSubtypePreference(getActivity(), this); getPreferenceScreen().addPreference(newSubtype); newSubtype.show(); mIsAddingNewSubtype = true; diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index 091ca43c6..768cba9b2 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -16,29 +16,36 @@ package com.android.inputmethod.latin.settings; +/** + * Debug settings for the application. + * + * Note: Even though these settings are stored in the default shared preferences file, + * they shouldn't be restored across devices. + * If a new key is added here, it should also be blacklisted for restore in + * {@link LocalSettingsConstants}. + */ public final class DebugSettings { public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY = "force_physical_keyboard_special_key"; - public static final String PREF_SHOULD_SHOW_LXX_SUGGESTION_UI = - "pref_should_show_lxx_suggestion_ui"; public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS = "pref_has_custom_key_preview_animation_params"; - public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE = - "pref_key_preview_show_up_start_x_scale"; - public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE = - "pref_key_preview_show_up_start_y_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = + "pref_key_preview_dismiss_duration"; public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE = "pref_key_preview_dismiss_end_x_scale"; public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE = "pref_key_preview_dismiss_end_y_scale"; public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION = "pref_key_preview_show_up_duration"; - public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = - "pref_key_preview_dismiss_duration"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE = + "pref_key_preview_show_up_start_x_scale"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE = + "pref_key_preview_show_up_start_y_scale"; + public static final String PREF_SHOULD_SHOW_LXX_SUGGESTION_UI = + "pref_should_show_lxx_suggestion_ui"; public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview"; - public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; private DebugSettings() { // This class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java index e9f8d45aa..475f1def7 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -73,7 +73,6 @@ public final class DebugSettingsFragment extends SubScreenFragment dictDumpPreferenceGroup.addPreference(pref); } final Resources res = getResources(); - setupKeyLongpressTimeoutSettings(); setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, @@ -163,45 +162,6 @@ public final class DebugSettingsFragment extends SubScreenFragment } } - private void setupKeyLongpressTimeoutSettings() { - final SharedPreferences prefs = getSharedPreferences(); - final Resources res = getResources(); - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( - DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT); - if (pref == null) { - return; - } - pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - @Override - public void writeValue(final int value, final String key) { - prefs.edit().putInt(key, value).apply(); - } - - @Override - public void writeDefaultValue(final String key) { - prefs.edit().remove(key).apply(); - } - - @Override - public int readValue(final String key) { - return Settings.readKeyLongpressTimeout(prefs, res); - } - - @Override - public int readDefaultValue(final String key) { - return Settings.readDefaultKeyLongpressTimeout(res); - } - - @Override - public String getValueText(final int value) { - return res.getString(R.string.abbreviation_unit_milliseconds, value); - } - - @Override - public void feedbackValue(final int value) {} - }); - } - private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) { final SharedPreferences prefs = getSharedPreferences(); final Resources res = getResources(); diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java new file mode 100644 index 000000000..c9e9dc8d9 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java @@ -0,0 +1,60 @@ +/* + * 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.settings; + +/** + * Collection of device specific preference constants. + */ +public class LocalSettingsConstants { + // Preference file for storing preferences that are tied to a device + // and are not backed up. + public static final String PREFS_FILE = "local_prefs"; + + // Preference key for the current account. + // Do not restore. + public static final String PREF_ACCOUNT_NAME = "pref_account_name"; + // Preference key for enabling cloud sync feature. + // Do not restore. + public static final String PREF_ENABLE_CLOUD_SYNC = "pref_enable_cloud_sync"; + + // List of preference keys to skip from being restored by backup agent. + // These preferences are tied to a device and hence should not be restored. + // e.g. account name. + // Ideally they could have been kept in a separate file that wasn't backed up + // however the preference UI currently only deals with the default + // shared preferences which makes it non-trivial to move these out to + // a different shared preferences file. + public static final String[] PREFS_TO_SKIP_RESTORING = new String[] { + PREF_ACCOUNT_NAME, + PREF_ENABLE_CLOUD_SYNC, + // The debug settings are not restored on a new device. + // If a feature relies on these, it should ensure that the defaults are + // correctly set for it to work on a new device. + DebugSettings.PREF_DEBUG_MODE, + DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, + DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY, + DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, + DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, + DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, + DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, + DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW + }; +} diff --git a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java index b073c50a4..b71f8829b 100644 --- a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java @@ -38,5 +38,6 @@ public final class MultiLingualSettingsFragment extends SubScreenFragment { removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY); removePreference(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); } + AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(getActivity(), this); } } diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java index 31a20c4db..7603dbba5 100644 --- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java +++ b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java @@ -22,7 +22,8 @@ public class NativeSuggestOptions { private static final int USE_FULL_EDIT_DISTANCE = 1; private static final int BLOCK_OFFENSIVE_WORDS = 2; private static final int SPACE_AWARE_GESTURE_ENABLED = 3; - private static final int OPTIONS_SIZE = 4; + 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]; @@ -43,6 +44,12 @@ public class NativeSuggestOptions { setBooleanOption(SPACE_AWARE_GESTURE_ENABLED, value); } + public void setWeightForLocale(final float value) { + // We're passing this option as a fixed point value, in thousands. This is decoded in + // native code by SuggestOptions#weightForLocale(). + setIntegerOption(WEIGHT_FOR_LOCALE_IN_THOUSANDS, (int) (value * 1000)); + } + public void setAdditionalFeaturesOptions(final int[] additionalOptions) { if (additionalOptions == null) { return; diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 84596b4ad..391fc1982 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -93,8 +93,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; - public static final String PREF_KEYPRESS_SOUND_VOLUME = - "pref_keypress_sound_volume"; + public static final String PREF_KEYPRESS_SOUND_VOLUME = "pref_keypress_sound_volume"; + public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = "pref_gesture_floating_preview_text"; @@ -106,8 +106,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal"; public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging"; - public static final String PREF_ACCOUNT_NAME = "pref_account_name"; - // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead. // This is being used only for the backward compatibility. private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY = @@ -319,7 +317,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static int readKeyLongpressTimeout(final SharedPreferences prefs, final Resources res) { final int milliseconds = prefs.getInt( - DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); + PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : readDefaultKeyLongpressTimeout(res); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index 8c4801798..6c21accf6 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -56,6 +56,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment { final Preference accountsPreference = findPreference(Settings.SCREEN_ACCOUNTS); preferenceScreen.removePreference(accountsPreference); } + AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(getActivity(), this); } @Override diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index ce8a0ab9c..660b4e095 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -64,7 +64,6 @@ public class SettingsValues { public final boolean mSoundOn; public final boolean mKeyPreviewPopupOn; public final boolean mShowsVoiceInputKey; - public final String mAccountName; public final boolean mIncludesOtherImesInLanguageSwitchList; public final boolean mShowsLanguageSwitchKey; public final boolean mUseContactsDict; @@ -141,7 +140,6 @@ public class SettingsValues { mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) && mInputAttributes.mShouldShowVoiceInputKey && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); - mAccountName = prefs.getString(Settings.PREF_ACCOUNT_NAME, null); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); @@ -385,8 +383,6 @@ public class SettingsValues { sb.append("" + mKeyPreviewPopupOn); sb.append("\n mShowsVoiceInputKey = "); sb.append("" + mShowsVoiceInputKey); - sb.append("\n mAccountName = "); - sb.append("" + mAccountName); sb.append("\n mIncludesOtherImesInLanguageSwitchList = "); sb.append("" + mIncludesOtherImesInLanguageSwitchList); sb.append("\n mShowsLanguageSwitchKey = "); diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java index 54562f39d..c3b30dcb4 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -42,6 +42,8 @@ import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; import java.util.ArrayList; +import javax.annotation.Nonnull; + // TODO: Use Fragment to implement welcome screen and setup steps. public final class SetupWizardActivity extends Activity implements View.OnClickListener { static final String TAG = SetupWizardActivity.class.getSimpleName(); @@ -82,7 +84,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL private final InputMethodManager mImmInHandler; - public SettingsPoolingHandler(final SetupWizardActivity ownerInstance, + public SettingsPoolingHandler(@Nonnull final SetupWizardActivity ownerInstance, final InputMethodManager imm) { super(ownerInstance); mImmInHandler = imm; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index d55939971..7b66bbb75 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -321,18 +321,6 @@ final class SuggestionStripLayoutHelper { } else { color = mColorSuggested; } - if (DebugFlags.DEBUG_ENABLED && suggestedWords.size() > 1) { - // If we auto-correct, then the autocorrection is in slot 0 and the typed word - // is in slot 1. - if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION - && suggestedWords.mWillAutoCorrect - && AutoCorrectionUtils.shouldBlockAutoCorrectionBySafetyNet( - suggestedWords.getLabel(SuggestedWords.INDEX_OF_AUTO_CORRECTION), - suggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD))) { - return 0xFFFF0000; - } - } - if (suggestedWords.mIsObsoleteSuggestions && !isTypedWord) { return applyAlpha(color, mAlphaObsoleted); } @@ -553,12 +541,12 @@ final class SuggestionStripLayoutHelper { return countInStrip; } - public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) { - final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent() - .mShouldShowLxxSuggestionUi; + public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip, + final boolean shouldShowWordToSave) { + final boolean showsHintWithWord = shouldShowWordToSave + || !Settings.getInstance().getCurrent().mShouldShowLxxSuggestionUi; final int stripWidth = addToDictionaryStrip.getWidth(); - final int width = shouldShowUiToAcceptTypedWord ? stripWidth - : stripWidth - mDividerWidth - mPadding * 2; + final int width = stripWidth - (showsHintWithWord ? mDividerWidth + mPadding * 2 : 0); final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save); wordView.setTextColor(mColorTypedWord); @@ -569,7 +557,7 @@ final class SuggestionStripLayoutHelper { wordView.setText(wordToSave); wordView.setTextScaleX(wordScaleX); setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); - final int wordVisibility = shouldShowUiToAcceptTypedWord ? View.GONE : View.VISIBLE; + final int wordVisibility = showsHintWithWord ? View.VISIBLE : View.GONE; wordView.setVisibility(wordVisibility); addToDictionaryStrip.findViewById(R.id.word_to_save_divider).setVisibility(wordVisibility); @@ -579,12 +567,7 @@ final class SuggestionStripLayoutHelper { final float hintWeight; final TextView hintView = (TextView)addToDictionaryStrip.findViewById( R.id.hint_add_to_dictionary); - if (shouldShowUiToAcceptTypedWord) { - hintText = res.getText(R.string.hint_add_to_dictionary_without_word); - hintWidth = width; - hintWeight = 1.0f; - hintView.setGravity(Gravity.CENTER); - } else { + if (showsHintWithWord) { final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip) == ViewCompat.LAYOUT_DIRECTION_RTL); final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW; @@ -595,6 +578,11 @@ final class SuggestionStripLayoutHelper { hintWidth = width - wordWidth; hintWeight = 1.0f - mCenterSuggestionWeight; hintView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START); + } else { + hintText = res.getText(R.string.hint_add_to_dictionary_without_word); + hintWidth = width; + hintWeight = 1.0f; + hintView.setGravity(Gravity.CENTER); } hintView.setTextColor(mColorAutoCorrect); final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index e40fd8800..789d549d7 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -231,8 +231,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return mStripVisibilityGroup.isShowingAddToDictionaryStrip(); } - public void showAddToDictionaryHint(final String word) { - mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip); + public void showAddToDictionaryHint(final String word, final boolean shouldShowWordToSave) { + mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, shouldShowWordToSave); // {@link TextView#setTag()} is used to hold the word to be added to dictionary. The word // will be extracted at {@link #onClick(View)}. mAddToDictionaryStrip.setTag(word); @@ -501,7 +501,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return; } final Object tag = view.getTag(); - // {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}. + // {@link String} tag is set at {@link #suggestAddingToDictionary(String,CharSequence)}. if (tag instanceof String) { final String wordToSave = (String)tag; mListener.addWordToUserDictionary(wordToSave); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java index 52708455e..5c86a02af 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java @@ -22,7 +22,7 @@ import com.android.inputmethod.latin.SuggestedWords; * An object that gives basic control of a suggestion strip and some info on it. */ public interface SuggestionStripViewAccessor { - public void showAddToDictionaryHint(final String word); + public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip); public boolean isShowingAddToDictionaryHint(); public void dismissAddToDictionaryHint(); public void setNeutralSuggestionStrip(); diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java index 156fcf57c..cba769521 100644 --- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java @@ -52,41 +52,9 @@ public final class AutoCorrectionUtils { if (DBG) { Log.d(TAG, "Auto corrected by S-threshold."); } - return !shouldBlockAutoCorrectionBySafetyNet(consideredWord, suggestion.mWord); + return true; } } return false; } - - // TODO: Resolve the inconsistencies between the native auto correction algorithms and - // this safety net - public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord, - final String suggestion) { - // Safety net for auto correction. - // Actually if we hit this safety net, it's a bug. - // If user selected aggressive auto correction mode, there is no need to use the safety - // net. - // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH, - // we should not use net because relatively edit distance can be big. - final int typedWordLength = typedWord.length(); - if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) { - return false; - } - final int maxEditDistanceOfNativeDictionary = (typedWordLength / 2) + 1; - final int distance = BinaryDictionaryUtils.editDistance(typedWord, suggestion); - if (DBG) { - Log.d(TAG, "Autocorrected edit distance = " + distance - + ", " + maxEditDistanceOfNativeDictionary); - } - if (distance > maxEditDistanceOfNativeDictionary) { - if (DBG) { - Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion); - Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. " - + "Turning off auto-correction."); - } - return true; - } else { - return false; - } - } } diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java index 5d7deba15..ce25fe6a4 100644 --- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java @@ -43,7 +43,6 @@ public final class BinaryDictionaryUtils { private static native boolean createEmptyDictFileNative(String filePath, long dictVersion, String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray); private static native float calcNormalizedScoreNative(int[] before, int[] after, int score); - private static native int editDistanceNative(int[] before, int[] after); private static native int setCurrentTimeForTestNative(int currentTime); public static DictionaryHeader getHeader(final File dictFile) @@ -112,14 +111,6 @@ public final class BinaryDictionaryUtils { StringUtils.toCodePointArray(after), score); } - public static int editDistance(final String before, final String after) { - if (before == null || after == null) { - throw new IllegalArgumentException(); - } - return editDistanceNative(StringUtils.toCodePointArray(before), - StringUtils.toCodePointArray(after)); - } - /** * Control the current time to be used in the native code. If currentTime >= 0, this method sets * the current time and gets into test mode. diff --git a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java index 9dc0524a2..e05618901 100644 --- a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java @@ -16,10 +16,12 @@ package com.android.inputmethod.latin.utils; +import android.annotation.TargetApi; import android.graphics.Matrix; import android.graphics.Rect; import android.inputmethodservice.ExtractEditText; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.text.Layout; import android.text.Spannable; import android.view.View; @@ -27,6 +29,12 @@ import android.view.ViewParent; import android.view.inputmethod.CursorAnchorInfo; import android.widget.TextView; +import com.android.inputmethod.compat.BuildCompatUtils; +import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * This class allows input methods to extract {@link CursorAnchorInfo} directly from the given * {@link TextView}. This is useful and even necessary to support full-screen mode where the default @@ -77,13 +85,32 @@ public final class CursorAnchorInfoUtils { } /** + * Extracts {@link CursorAnchorInfoCompatWrapper} from the given {@link TextView}. + * @param textView the target text view from which {@link CursorAnchorInfoCompatWrapper} is to + * be extracted. + * @return the {@link CursorAnchorInfoCompatWrapper} object based on the current layout. + * {@code null} if {@code Build.VERSION.SDK_INT} is 20 or prior or {@link TextView} is not + * ready to provide layout information. + */ + @Nullable + public static CursorAnchorInfoCompatWrapper extractFromTextView( + @Nonnull final TextView textView) { + if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + return null; + } + return CursorAnchorInfoCompatWrapper.wrap(extractFromTextViewInternal(textView)); + } + + /** * Returns {@link CursorAnchorInfo} from the given {@link TextView}. * @param textView the target text view from which {@link CursorAnchorInfo} is to be extracted. * @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it * is not feasible. */ - public static CursorAnchorInfo getCursorAnchorInfo(final TextView textView) { - Layout layout = textView.getLayout(); + @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @Nullable + private static CursorAnchorInfo extractFromTextViewInternal(@Nonnull final TextView textView) { + final Layout layout = textView.getLayout(); if (layout == null) { return null; } diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index 249478785..e29aabacd 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -230,6 +230,7 @@ public class DictionaryInfoUtils { /** * Helper method to return a dictionary res id for a locale, or 0 if none. + * @param res resources for the app * @param locale dictionary locale * @return main dictionary resource id */ @@ -258,6 +259,7 @@ public class DictionaryInfoUtils { /** * Returns a main dictionary resource id + * @param res resources for the app * @param locale dictionary locale * @return main dictionary resource id */ @@ -283,6 +285,25 @@ public class DictionaryInfoUtils { BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.getLanguage().toString(); } + /** + * Returns whether a main dictionary is readily available for this locale. + * + * This does not query the content provider. + * + * @param context context to open files upon + * @param locale dictionary locale + * @return true if a dictionary is available right away, false otherwise + */ + public static boolean hasReadilyAvailableMainDictionaryForLocale(final Context context, + final Locale locale) { + final Resources res = context.getResources(); + if (0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale)) { + return true; + } + final String fileName = getCacheFileName(getMainDictId(locale), locale.toString(), context); + return new File(fileName).exists(); + } + public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file) { return getDictionaryFileHeaderOrNull(file, 0, file.length()); } diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java index dd6fac671..9a5be99b3 100644 --- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java +++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java @@ -21,21 +21,22 @@ import android.os.Looper; import java.lang.ref.WeakReference; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public class LeakGuardHandlerWrapper<T> extends Handler { private final WeakReference<T> mOwnerInstanceRef; - public LeakGuardHandlerWrapper(final T ownerInstance) { + public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance) { this(ownerInstance, Looper.myLooper()); } - public LeakGuardHandlerWrapper(final T ownerInstance, final Looper looper) { + public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance, final Looper looper) { super(looper); - if (ownerInstance == null) { - throw new NullPointerException("ownerInstance is null"); - } mOwnerInstanceRef = new WeakReference<>(ownerInstance); } + @Nullable public T getOwnerInstance() { return mOwnerInstanceRef.get(); } diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 76c7fdd6f..f8dadb488 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -28,7 +28,7 @@ #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" #include "suggest/core/result/suggestion_results.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/core/suggest_options.h" #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h" #include "utils/char_utils.h" @@ -242,15 +242,15 @@ static void latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, env->GetFloatArrayRegion(inOutWeightOfLangModelVsSpatialModel, 0, 1 /* len */, &weightOfLangModelVsSpatialModel); SuggestionResults suggestionResults(MAX_RESULTS); - const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, + const NgramContext ngramContext = JniDataUtils::constructNgramContext(env, prevWordCodePointArrays, isBeginningOfSentenceArray, prevWordCount); if (givenSuggestOptions.isGesture() || inputSize > 0) { // TODO: Use SuggestionResults to return suggestions. dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, - times, pointerIds, inputCodePoints, inputSize, &prevWordsInfo, + times, pointerIds, inputCodePoints, inputSize, &ngramContext, &givenSuggestOptions, weightOfLangModelVsSpatialModel, &suggestionResults); } else { - dictionary->getPredictions(&prevWordsInfo, &suggestionResults); + dictionary->getPredictions(&ngramContext, &suggestionResults); } if (DEBUG_DICT) { suggestionResults.dumpSuggestions(); @@ -289,10 +289,10 @@ static jint latinime_BinaryDictionary_getNgramProbability(JNIEnv *env, jclass cl const jsize wordLength = env->GetArrayLength(word); int wordCodePoints[wordLength]; env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints); - const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, + const NgramContext ngramContext = JniDataUtils::constructNgramContext(env, prevWordCodePointArrays, isBeginningOfSentenceArray, env->GetArrayLength(prevWordCodePointArrays)); - return dictionary->getNgramProbability(&prevWordsInfo, + return dictionary->getNgramProbability(&ngramContext, CodePointArrayView(wordCodePoints, wordLength)); } @@ -327,8 +327,9 @@ static jint latinime_BinaryDictionary_getNextWord(JNIEnv *env, jclass clazz, static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz, jlong dict, jintArray word, jboolean isBeginningOfSentence, jintArray outCodePoints, - jbooleanArray outFlags, jintArray outProbabilityInfo, jobject outBigramTargets, - jobject outBigramProbabilityInfo, jobject outShortcutTargets, + jbooleanArray outFlags, jintArray outProbabilityInfo, jobject /* outNgramPrevWordsArray */, + jobject /* outNgramPrevWordIsBeginningOfSentenceArray */, jobject outNgramTargets, + jobject outNgramProbabilityInfo, jobject outShortcutTargets, jobject outShortcutProbabilities) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) return; @@ -351,7 +352,7 @@ static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz, const WordProperty wordProperty = dictionary->getWordProperty( CodePointArrayView(wordCodePoints, codePointCount)); wordProperty.outputProperties(env, outCodePoints, outFlags, outProbabilityInfo, - outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, + outNgramTargets, outNgramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); } @@ -401,7 +402,7 @@ static bool latinime_BinaryDictionary_addNgramEntry(JNIEnv *env, jclass clazz, j if (!dictionary) { return false; } - const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, + const NgramContext ngramContext = JniDataUtils::constructNgramContext(env, prevWordCodePointArrays, isBeginningOfSentenceArray, env->GetArrayLength(prevWordCodePointArrays)); jsize wordLength = env->GetArrayLength(word); @@ -410,7 +411,7 @@ static bool latinime_BinaryDictionary_addNgramEntry(JNIEnv *env, jclass clazz, j // Use 1 for count to indicate the ngram has inputted. const NgramProperty ngramProperty(CodePointArrayView(wordCodePoints, wordLength).toVector(), probability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */)); - return dictionary->addNgramEntry(&prevWordsInfo, &ngramProperty); + return dictionary->addNgramEntry(&ngramContext, &ngramProperty); } static bool latinime_BinaryDictionary_removeNgramEntry(JNIEnv *env, jclass clazz, jlong dict, @@ -420,31 +421,32 @@ static bool latinime_BinaryDictionary_removeNgramEntry(JNIEnv *env, jclass clazz if (!dictionary) { return false; } - const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, + const NgramContext ngramContext = JniDataUtils::constructNgramContext(env, prevWordCodePointArrays, isBeginningOfSentenceArray, env->GetArrayLength(prevWordCodePointArrays)); jsize codePointCount = env->GetArrayLength(word); int wordCodePoints[codePointCount]; env->GetIntArrayRegion(word, 0, codePointCount, wordCodePoints); - return dictionary->removeNgramEntry(&prevWordsInfo, + return dictionary->removeNgramEntry(&ngramContext, CodePointArrayView(wordCodePoints, codePointCount)); } -static bool latinime_BinaryDictionary_updateCounter(JNIEnv *env, jclass clazz, jlong dict, - jobjectArray prevWordCodePointArrays, jbooleanArray isBeginningOfSentenceArray, - jintArray word, jboolean isValidWord, jint count, jint timestamp) { +static bool latinime_BinaryDictionary_updateEntriesForWordWithNgramContext(JNIEnv *env, + jclass clazz, jlong dict, jobjectArray prevWordCodePointArrays, + jbooleanArray isBeginningOfSentenceArray, jintArray word, jboolean isValidWord, jint count, + jint timestamp) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { return false; } - const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, + const NgramContext ngramContext = JniDataUtils::constructNgramContext(env, prevWordCodePointArrays, isBeginningOfSentenceArray, env->GetArrayLength(prevWordCodePointArrays)); jsize codePointCount = env->GetArrayLength(word); int wordCodePoints[codePointCount]; env->GetIntArrayRegion(word, 0, codePointCount, wordCodePoints); const HistoricalInfo historicalInfo(timestamp, 0 /* level */, count); - return dictionary->updateCounter(&prevWordsInfo, + return dictionary->updateEntriesForWordWithNgramContext(&ngramContext, CodePointArrayView(wordCodePoints, codePointCount), isValidWord == JNI_TRUE, historicalInfo); } @@ -527,9 +529,9 @@ static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, j const NgramProperty ngramProperty( CodePointArrayView(word1CodePoints, word1Length).toVector(), bigramProbability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */)); - const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, + const NgramContext ngramContext(word0CodePoints, word0Length, false /* isBeginningOfSentence */); - dictionary->addNgramEntry(&prevWordsInfo, &ngramProperty); + dictionary->addNgramEntry(&ngramContext, &ngramProperty); } if (dictionary->needsToRunGC(true /* mindsBlockByGC */)) { return i + 1; @@ -639,10 +641,10 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j return false; } } - const PrevWordsInfo prevWordsInfo(wordCodePoints, wordCodePointCount, + const NgramContext ngramContext(wordCodePoints, wordCodePointCount, wordProperty.getUnigramProperty()->representsBeginningOfSentence()); for (const NgramProperty &ngramProperty : *wordProperty.getNgramProperties()) { - if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&prevWordsInfo, + if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&ngramContext, &ngramProperty)) { LogUtils::logToJava(env, "Cannot add ngram to the new dict."); return false; @@ -718,7 +720,8 @@ static const JNINativeMethod sMethods[] = { { const_cast<char *>("getWordPropertyNative"), const_cast<char *>("(J[IZ[I[Z[ILjava/util/ArrayList;Ljava/util/ArrayList;" - "Ljava/util/ArrayList;Ljava/util/ArrayList;)V"), + "Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;" + "Ljava/util/ArrayList;)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_getWordProperty) }, { @@ -747,9 +750,9 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_removeNgramEntry) }, { - const_cast<char *>("updateCounterNative"), + const_cast<char *>("updateEntriesForWordWithNgramContextNative"), const_cast<char *>("(J[[I[Z[IZII)Z"), - reinterpret_cast<void *>(latinime_BinaryDictionary_updateCounter) + reinterpret_cast<void *>(latinime_BinaryDictionary_updateEntriesForWordWithNgramContext) }, { const_cast<char *>("addMultipleDictionaryEntriesNative"), diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp index 0a34b783a..68bf417e5 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp @@ -68,18 +68,6 @@ static jfloat latinime_BinaryDictionaryUtils_calcNormalizedScore(JNIEnv *env, jc afterCodePoints, afterLength, score); } -static jint latinime_BinaryDictionaryUtils_editDistance(JNIEnv *env, jclass clazz, jintArray before, - jintArray after) { - jsize beforeLength = env->GetArrayLength(before); - jsize afterLength = env->GetArrayLength(after); - int beforeCodePoints[beforeLength]; - int afterCodePoints[afterLength]; - env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints); - env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints); - return AutocorrectionThresholdUtils::editDistance(beforeCodePoints, beforeLength, - afterCodePoints, afterLength); -} - static int latinime_BinaryDictionaryUtils_setCurrentTimeForTest(JNIEnv *env, jclass clazz, jint currentTime) { if (currentTime >= 0) { @@ -104,11 +92,6 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionaryUtils_calcNormalizedScore) }, { - const_cast<char *>("editDistanceNative"), - const_cast<char *>("([I[I)I"), - reinterpret_cast<void *>(latinime_BinaryDictionaryUtils_editDistance) - }, - { const_cast<char *>("setCurrentTimeForTestNative"), const_cast<char *>("(I)I"), reinterpret_cast<void *>(latinime_BinaryDictionaryUtils_setCurrentTimeForTest) diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 766064153..3c6bff3b6 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -22,7 +22,7 @@ #include "jni.h" #include "jni_common.h" #include "suggest/core/session/dic_traverse_session.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" namespace latinime { class Dictionary; @@ -40,14 +40,14 @@ static void latinime_initDicTraverseSession(JNIEnv *env, jclass clazz, jlong tra } Dictionary *dict = reinterpret_cast<Dictionary *>(dictionary); if (!previousWord) { - PrevWordsInfo prevWordsInfo; - ts->init(dict, &prevWordsInfo, 0 /* suggestOptions */); + NgramContext emptyNgramContext; + ts->init(dict, &emptyNgramContext, 0 /* suggestOptions */); return; } int prevWord[previousWordLength]; env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord); - PrevWordsInfo prevWordsInfo(prevWord, previousWordLength, false /* isStartOfSentence */); - ts->init(dict, &prevWordsInfo, 0 /* suggestOptions */); + NgramContext ngramContext(prevWord, previousWordLength, false /* isStartOfSentence */); + ts->init(dict, &ngramContext, 0 /* suggestOptions */); } static void latinime_releaseDicTraverseSession(JNIEnv *env, jclass clazz, jlong traverseSession) { diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 7a69d3ceb..697e99ffb 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -23,7 +23,7 @@ #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/result/suggestion_results.h" #include "suggest/core/session/dic_traverse_session.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/core/suggest.h" #include "suggest/core/suggest_options.h" #include "suggest/policyimpl/gesture/gesture_suggest_policy_factory.h" @@ -46,11 +46,11 @@ Dictionary::Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::Structu void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, - int inputSize, const PrevWordsInfo *const prevWordsInfo, + int inputSize, const NgramContext *const ngramContext, const SuggestOptions *const suggestOptions, const float weightOfLangModelVsSpatialModel, SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); - traverseSession->init(this, prevWordsInfo, suggestOptions); + traverseSession->init(this, ngramContext, suggestOptions); const auto &suggest = suggestOptions->isGesture() ? mGestureSuggest : mTypingSuggest; suggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, @@ -58,10 +58,10 @@ void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession } Dictionary::NgramListenerForPrediction::NgramListenerForPrediction( - const PrevWordsInfo *const prevWordsInfo, const WordIdArrayView prevWordIds, + const NgramContext *const ngramContext, const WordIdArrayView prevWordIds, SuggestionResults *const suggestionResults, const DictionaryStructureWithBufferPolicy *const dictStructurePolicy) - : mPrevWordsInfo(prevWordsInfo), mPrevWordIds(prevWordIds), + : mNgramContext(ngramContext), mPrevWordIds(prevWordIds), mSuggestionResults(suggestionResults), mDictStructurePolicy(dictStructurePolicy) {} void Dictionary::NgramListenerForPrediction::onVisitEntry(const int ngramProbability, @@ -69,7 +69,7 @@ void Dictionary::NgramListenerForPrediction::onVisitEntry(const int ngramProbabi if (targetWordId == NOT_A_WORD_ID) { return; } - if (mPrevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */) + if (mNgramContext->isNthPrevWordBeginningOfSentence(1 /* n */) && ngramProbability == NOT_A_PROBABILITY) { return; } @@ -85,20 +85,20 @@ void Dictionary::NgramListenerForPrediction::onVisitEntry(const int ngramProbabi wordAttributes.getProbability()); } -void Dictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo, +void Dictionary::getPredictions(const NgramContext *const ngramContext, SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds( + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds( mDictionaryStructureWithBufferPolicy.get(), &prevWordIdArray, true /* tryLowerCaseSearch */); - NgramListenerForPrediction listener(prevWordsInfo, prevWordIds, outSuggestionResults, + NgramListenerForPrediction listener(ngramContext, prevWordIds, outSuggestionResults, mDictionaryStructureWithBufferPolicy.get()); mDictionaryStructureWithBufferPolicy->iterateNgramEntries(prevWordIds, &listener); } int Dictionary::getProbability(const CodePointArrayView codePoints) const { - return getNgramProbability(nullptr /* prevWordsInfo */, codePoints); + return getNgramProbability(nullptr /* ngramContext */, codePoints); } int Dictionary::getMaxProbabilityOfExactMatches(const CodePointArrayView codePoints) const { @@ -107,18 +107,18 @@ int Dictionary::getMaxProbabilityOfExactMatches(const CodePointArrayView codePoi mDictionaryStructureWithBufferPolicy.get(), codePoints); } -int Dictionary::getNgramProbability(const PrevWordsInfo *const prevWordsInfo, +int Dictionary::getNgramProbability(const NgramContext *const ngramContext, const CodePointArrayView codePoints) const { TimeKeeper::setCurrentTime(); const int wordId = mDictionaryStructureWithBufferPolicy->getWordId(codePoints, false /* forceLowerCaseSearch */); if (wordId == NOT_A_WORD_ID) return NOT_A_PROBABILITY; - if (!prevWordsInfo) { + if (!ngramContext) { return getDictionaryStructurePolicy()->getProbabilityOfWord(WordIdArrayView(), wordId); } WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds - (mDictionaryStructureWithBufferPolicy.get(), &prevWordIdArray, + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds( + mDictionaryStructureWithBufferPolicy.get(), &prevWordIdArray, true /* tryLowerCaseSearch */); return getDictionaryStructurePolicy()->getProbabilityOfWord(prevWordIds, wordId); } @@ -140,24 +140,24 @@ bool Dictionary::removeUnigramEntry(const CodePointArrayView codePoints) { return mDictionaryStructureWithBufferPolicy->removeUnigramEntry(codePoints); } -bool Dictionary::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, +bool Dictionary::addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->addNgramEntry(prevWordsInfo, ngramProperty); + return mDictionaryStructureWithBufferPolicy->addNgramEntry(ngramContext, ngramProperty); } -bool Dictionary::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, +bool Dictionary::removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView codePoints) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, codePoints); + return mDictionaryStructureWithBufferPolicy->removeNgramEntry(ngramContext, codePoints); } -bool Dictionary::updateCounter(const PrevWordsInfo *const prevWordsInfo, +bool Dictionary::updateEntriesForWordWithNgramContext(const NgramContext *const ngramContext, const CodePointArrayView codePoints, const bool isValidWord, const HistoricalInfo historicalInfo) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->updateCounter(prevWordsInfo, codePoints, - isValidWord, historicalInfo); + return mDictionaryStructureWithBufferPolicy->updateEntriesForWordWithNgramContext(ngramContext, + codePoints, isValidWord, historicalInfo); } bool Dictionary::flush(const char *const filePath) { diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index a58dbfbd7..843aec473 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -33,7 +33,7 @@ namespace latinime { class DictionaryStructureWithBufferPolicy; class DicTraverseSession; -class PrevWordsInfo; +class NgramContext; class ProximityInfo; class SuggestionResults; class SuggestOptions; @@ -66,18 +66,18 @@ class Dictionary { void getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, - int inputSize, const PrevWordsInfo *const prevWordsInfo, + int inputSize, const NgramContext *const ngramContext, const SuggestOptions *const suggestOptions, const float weightOfLangModelVsSpatialModel, SuggestionResults *const outSuggestionResults) const; - void getPredictions(const PrevWordsInfo *const prevWordsInfo, + void getPredictions(const NgramContext *const ngramContext, SuggestionResults *const outSuggestionResults) const; int getProbability(const CodePointArrayView codePoints) const; int getMaxProbabilityOfExactMatches(const CodePointArrayView codePoints) const; - int getNgramProbability(const PrevWordsInfo *const prevWordsInfo, + int getNgramProbability(const NgramContext *const ngramContext, const CodePointArrayView codePoints) const; bool addUnigramEntry(const CodePointArrayView codePoints, @@ -85,13 +85,13 @@ class Dictionary { bool removeUnigramEntry(const CodePointArrayView codePoints); - bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty); - bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView codePoints); - bool updateCounter(const PrevWordsInfo *const prevWordsInfo, + bool updateEntriesForWordWithNgramContext(const NgramContext *const ngramContext, const CodePointArrayView codePoints, const bool isValidWord, const HistoricalInfo historicalInfo); @@ -123,7 +123,7 @@ class Dictionary { class NgramListenerForPrediction : public NgramListener { public: - NgramListenerForPrediction(const PrevWordsInfo *const prevWordsInfo, + NgramListenerForPrediction(const NgramContext *const ngramContext, const WordIdArrayView prevWordIds, SuggestionResults *const suggestionResults, const DictionaryStructureWithBufferPolicy *const dictStructurePolicy); virtual void onVisitEntry(const int ngramProbability, const int targetWordId); @@ -131,7 +131,7 @@ class Dictionary { private: DISALLOW_IMPLICIT_CONSTRUCTORS(NgramListenerForPrediction); - const PrevWordsInfo *const mPrevWordsInfo; + const NgramContext *const mNgramContext; const WordIdArrayView mPrevWordIds; SuggestionResults *const mSuggestionResults; const DictionaryStructureWithBufferPolicy *const mDictStructurePolicy; diff --git a/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp b/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp index b85f3622a..9573c37bc 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp @@ -21,7 +21,7 @@ #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/dictionary/digraph_utils.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "utils/int_array_view.h" @@ -33,10 +33,10 @@ namespace latinime { std::vector<DicNode> current; std::vector<DicNode> next; - // No prev words information. - PrevWordsInfo emptyPrevWordsInfo; + // No ngram context. + NgramContext emptyNgramContext; WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = emptyPrevWordsInfo.getPrevWordIds( + const WordIdArrayView prevWordIds = emptyNgramContext.getPrevWordIds( dictionaryStructurePolicy, &prevWordIdArray, false /* tryLowerCaseSearch */); current.emplace_back(); DicNodeUtils::initAsRoot(dictionaryStructurePolicy, prevWordIds, ¤t.front()); diff --git a/native/jni/src/suggest/core/dictionary/property/historical_info.h b/native/jni/src/suggest/core/dictionary/property/historical_info.h index 5ed9ebfca..f9bd6fd8c 100644 --- a/native/jni/src/suggest/core/dictionary/property/historical_info.h +++ b/native/jni/src/suggest/core/dictionary/property/historical_info.h @@ -47,12 +47,12 @@ class HistoricalInfo { } private: - // Default copy constructor and assign operator are used for using in std::vector. + // Default copy constructor is used for using in std::vector. + DISALLOW_ASSIGNMENT_OPERATOR(HistoricalInfo); - // TODO: Make members const. - int mTimestamp; - int mLevel; - int mCount; + const int mTimestamp; + const int mLevel; + const int mCount; }; } // namespace latinime #endif /* LATINIME_HISTORICAL_INFO_H */ diff --git a/native/jni/src/suggest/core/dictionary/property/ngram_property.h b/native/jni/src/suggest/core/dictionary/property/ngram_property.h index dce460099..8709799f9 100644 --- a/native/jni/src/suggest/core/dictionary/property/ngram_property.h +++ b/native/jni/src/suggest/core/dictionary/property/ngram_property.h @@ -44,13 +44,13 @@ class NgramProperty { } private: - // Default copy constructor and assign operator are used for using in std::vector. + // Default copy constructor is used for using in std::vector. DISALLOW_DEFAULT_CONSTRUCTOR(NgramProperty); + DISALLOW_ASSIGNMENT_OPERATOR(NgramProperty); - // TODO: Make members const. - std::vector<int> mTargetCodePoints; - int mProbability; - HistoricalInfo mHistoricalInfo; + const std::vector<int> mTargetCodePoints; + const int mProbability; + const HistoricalInfo mHistoricalInfo; }; } // namespace latinime #endif // LATINIME_NGRAM_PROPERTY_H diff --git a/native/jni/src/suggest/core/dictionary/property/unigram_property.h b/native/jni/src/suggest/core/dictionary/property/unigram_property.h index d1f0ab4ca..5ed2e2602 100644 --- a/native/jni/src/suggest/core/dictionary/property/unigram_property.h +++ b/native/jni/src/suggest/core/dictionary/property/unigram_property.h @@ -41,12 +41,11 @@ class UnigramProperty { } private: - // Default copy constructor and assign operator are used for using in std::vector. + // Default copy constructor is used for using in std::vector. DISALLOW_DEFAULT_CONSTRUCTOR(ShortcutProperty); - // TODO: Make members const. - std::vector<int> mTargetCodePoints; - int mProbability; + const std::vector<int> mTargetCodePoints; + const int mProbability; }; UnigramProperty() @@ -104,13 +103,12 @@ class UnigramProperty { // Default copy constructor is used for using as a return value. DISALLOW_ASSIGNMENT_OPERATOR(UnigramProperty); - // TODO: Make members const. - bool mRepresentsBeginningOfSentence; - bool mIsNotAWord; - bool mIsBlacklisted; - int mProbability; - HistoricalInfo mHistoricalInfo; - std::vector<ShortcutProperty> mShortcuts; + const bool mRepresentsBeginningOfSentence; + const bool mIsNotAWord; + const bool mIsBlacklisted; + const int mProbability; + const HistoricalInfo mHistoricalInfo; + const std::vector<ShortcutProperty> mShortcuts; }; } // namespace latinime #endif // LATINIME_UNIGRAM_PROPERTY_H diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h index 6624b7921..ceda5c03f 100644 --- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h @@ -33,7 +33,7 @@ class DicNodeVector; class DictionaryHeaderStructurePolicy; class MultiBigramMap; class NgramListener; -class PrevWordsInfo; +class NgramContext; class UnigramProperty; /* @@ -81,15 +81,15 @@ class DictionaryStructureWithBufferPolicy { virtual bool removeUnigramEntry(const CodePointArrayView wordCodePoints) = 0; // Returns whether the update was success or not. - virtual bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + virtual bool addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty) = 0; // Returns whether the update was success or not. - virtual bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + virtual bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints) = 0; // Returns whether the update was success or not. - virtual bool updateCounter(const PrevWordsInfo *const prevWordsInfo, + virtual bool updateEntriesForWordWithNgramContext(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints, const bool isValidWord, const HistoricalInfo historicalInfo) = 0; diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h index 6dfa7e314..5b6616d9a 100644 --- a/native/jni/src/suggest/core/policy/traversal.h +++ b/native/jni/src/suggest/core/policy/traversal.h @@ -44,7 +44,7 @@ class Traversal { virtual bool needsToTraverseAllUserInput() const = 0; virtual float getMaxSpatialDistance() const = 0; virtual int getDefaultExpandDicNodeSize() const = 0; - virtual int getMaxCacheSize(const int inputSize) const = 0; + virtual int getMaxCacheSize(const int inputSize, const float weightForLocale) const = 0; virtual int getTerminalCacheSize() const = 0; virtual bool isPossibleOmissionChildNode(const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0; diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index c202b81fe..a06e7d070 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -110,10 +110,14 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return weighting->getOmissionCost(parentDicNode, dicNode); case CT_ADDITIONAL_PROXIMITY: // only used for typing - return weighting->getAdditionalProximityCost(); + // TODO: Quit calling getMatchedCost(). + return weighting->getAdditionalProximityCost() + + weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_SUBSTITUTION: // only used for typing - return weighting->getSubstitutionCost(); + // TODO: Quit calling getMatchedCost(). + return weighting->getSubstitutionCost() + + weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_NEW_WORD_SPACE_OMISSION: return weighting->getNewWordSpatialCost(traverseSession, dicNode, inputStateG); case CT_MATCH: @@ -176,9 +180,9 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_OMISSION: return 0; case CT_ADDITIONAL_PROXIMITY: - return 0; /* 0 because CT_MATCH will be called */ + return 1; case CT_SUBSTITUTION: - return 0; /* 0 because CT_MATCH will be called */ + return 1; case CT_NEW_WORD_SPACE_OMISSION: return 0; case CT_MATCH: diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index b4d01d0f0..52dc2f86c 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -20,7 +20,7 @@ #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" namespace latinime { @@ -30,12 +30,12 @@ const int DicTraverseSession::DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_S 256 * 1024; void DicTraverseSession::init(const Dictionary *const dictionary, - const PrevWordsInfo *const prevWordsInfo, const SuggestOptions *const suggestOptions) { + const NgramContext *const ngramContext, const SuggestOptions *const suggestOptions) { mDictionary = dictionary; mMultiWordCostMultiplier = getDictionaryStructurePolicy()->getHeaderStructurePolicy() ->getMultiWordCostMultiplier(); mSuggestOptions = suggestOptions; - mPrevWordIdCount = prevWordsInfo->getPrevWordIds(getDictionaryStructurePolicy(), + mPrevWordIdCount = ngramContext->getPrevWordIds(getDictionaryStructurePolicy(), &mPrevWordIdArray, true /* tryLowerCaseSearch */).size(); } diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index 9f841aa3c..bc53167f0 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -30,7 +30,7 @@ namespace latinime { class Dictionary; class DictionaryStructureWithBufferPolicy; -class PrevWordsInfo; +class NgramContext; class ProximityInfo; class SuggestOptions; @@ -61,7 +61,7 @@ class DicTraverseSession { // Non virtual inline destructor -- never inherit this class AK_FORCE_INLINE ~DicTraverseSession() {} - void init(const Dictionary *dictionary, const PrevWordsInfo *const prevWordsInfo, + void init(const Dictionary *dictionary, const NgramContext *const ngramContext, const SuggestOptions *const suggestOptions); // TODO: Remove and merge into init void setupForGetSuggestions(const ProximityInfo *pInfo, const int *inputCodePoints, diff --git a/native/jni/src/suggest/core/session/prev_words_info.h b/native/jni/src/suggest/core/session/ngram_context.h index 553d5ad07..64c71410f 100644 --- a/native/jni/src/suggest/core/session/prev_words_info.h +++ b/native/jni/src/suggest/core/session/ngram_context.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef LATINIME_PREV_WORDS_INFO_H -#define LATINIME_PREV_WORDS_INFO_H +#ifndef LATINIME_NGRAM_CONTEXT_H +#define LATINIME_NGRAM_CONTEXT_H #include <array> @@ -26,25 +26,26 @@ namespace latinime { -class PrevWordsInfo { +// Rename to NgramContext. +class NgramContext { public: // No prev word information. - PrevWordsInfo() : mPrevWordCount(0) { + NgramContext() : mPrevWordCount(0) { clear(); } - PrevWordsInfo(const PrevWordsInfo &prevWordsInfo) - : mPrevWordCount(prevWordsInfo.mPrevWordCount) { + NgramContext(const NgramContext &ngramContext) + : mPrevWordCount(ngramContext.mPrevWordCount) { for (size_t i = 0; i < mPrevWordCount; ++i) { - mPrevWordCodePointCount[i] = prevWordsInfo.mPrevWordCodePointCount[i]; - memmove(mPrevWordCodePoints[i], prevWordsInfo.mPrevWordCodePoints[i], + mPrevWordCodePointCount[i] = ngramContext.mPrevWordCodePointCount[i]; + memmove(mPrevWordCodePoints[i], ngramContext.mPrevWordCodePoints[i], sizeof(mPrevWordCodePoints[i][0]) * mPrevWordCodePointCount[i]); - mIsBeginningOfSentence[i] = prevWordsInfo.mIsBeginningOfSentence[i]; + mIsBeginningOfSentence[i] = ngramContext.mIsBeginningOfSentence[i]; } } // Construct from previous words. - PrevWordsInfo(const int prevWordCodePoints[][MAX_WORD_LENGTH], + NgramContext(const int prevWordCodePoints[][MAX_WORD_LENGTH], const int *const prevWordCodePointCount, const bool *const isBeginningOfSentence, const size_t prevWordCount) : mPrevWordCount(std::min(NELEMS(mPrevWordCodePoints), prevWordCount)) { @@ -61,7 +62,7 @@ class PrevWordsInfo { } // Construct from a previous word. - PrevWordsInfo(const int *const prevWordCodePoints, const int prevWordCodePointCount, + NgramContext(const int *const prevWordCodePoints, const int prevWordCodePointCount, const bool isBeginningOfSentence) : mPrevWordCount(1) { clear(); if (prevWordCodePointCount > MAX_WORD_LENGTH || !prevWordCodePoints) { @@ -78,8 +79,8 @@ class PrevWordsInfo { } // TODO: Remove. - const PrevWordsInfo getTrimmedPrevWordsInfo(const size_t maxPrevWordCount) const { - return PrevWordsInfo(mPrevWordCodePoints, mPrevWordCodePointCount, mIsBeginningOfSentence, + const NgramContext getTrimmedNgramContext(const size_t maxPrevWordCount) const { + return NgramContext(mPrevWordCodePoints, mPrevWordCodePointCount, mIsBeginningOfSentence, std::min(mPrevWordCount, maxPrevWordCount)); } @@ -122,7 +123,7 @@ class PrevWordsInfo { } private: - DISALLOW_ASSIGNMENT_OPERATOR(PrevWordsInfo); + DISALLOW_ASSIGNMENT_OPERATOR(NgramContext); static int getWordId(const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, const int *const wordCodePoints, const int wordCodePointCount, @@ -165,4 +166,4 @@ class PrevWordsInfo { bool mIsBeginningOfSentence[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; }; } // namespace latinime -#endif // LATINIME_PREV_WORDS_INFO_H +#endif // LATINIME_NGRAM_CONTEXT_H diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 457414f2b..c71526293 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -28,6 +28,7 @@ #include "suggest/core/policy/weighting.h" #include "suggest/core/result/suggestions_output_utils.h" #include "suggest/core/session/dic_traverse_session.h" +#include "suggest/core/suggest_options.h" namespace latinime { @@ -88,7 +89,8 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession) const { traverseSession->getDicTraverseCache()->continueSearch(); } else { // Restart recognition at the root. - traverseSession->resetCache(TRAVERSAL->getMaxCacheSize(traverseSession->getInputSize()), + traverseSession->resetCache(TRAVERSAL->getMaxCacheSize(traverseSession->getInputSize(), + traverseSession->getSuggestOptions()->weightForLocale()), TRAVERSAL->getTerminalCacheSize()); // Create a new dic node here DicNode rootNode; @@ -282,7 +284,6 @@ void Suggest::processDicNodeAsAdditionalProximityChar(DicTraverseSession *traver // not treat the node as a terminal. There is no need to pass the bigram map in these cases. Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_ADDITIONAL_PROXIMITY, traverseSession, dicNode, childDicNode, 0 /* multiBigramMap */); - weightChildNode(traverseSession, childDicNode); processExpandedDicNode(traverseSession, childDicNode); } @@ -290,7 +291,6 @@ void Suggest::processDicNodeAsSubstitution(DicTraverseSession *traverseSession, DicNode *dicNode, DicNode *childDicNode) const { Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SUBSTITUTION, traverseSession, dicNode, childDicNode, 0 /* multiBigramMap */); - weightChildNode(traverseSession, childDicNode); processExpandedDicNode(traverseSession, childDicNode); } @@ -401,7 +401,7 @@ void Suggest::weightChildNode(DicTraverseSession *traverseSession, DicNode *dicN if (dicNode->isCompletion(inputSize)) { Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_COMPLETION, traverseSession, 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */); - } else { // completion + } else { Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_MATCH, traverseSession, 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */); } diff --git a/native/jni/src/suggest/core/suggest_options.h b/native/jni/src/suggest/core/suggest_options.h index d456680dd..4d331292b 100644 --- a/native/jni/src/suggest/core/suggest_options.h +++ b/native/jni/src/suggest/core/suggest_options.h @@ -42,6 +42,12 @@ class SuggestOptions{ return getBoolOption(SPACE_AWARE_GESTURE_ENABLED); } + AK_FORCE_INLINE float weightForLocale() const { + // The weight is in thousands and we want the real value, so we divide by 1000. + // NativeSuggestOptions#setWeightForLocale does the opposite processing in Java. + return static_cast<float>(getIntOption(WEIGHT_FOR_LOCALE_IN_THOUSANDS)) / 1000.0f; + } + AK_FORCE_INLINE bool getAdditionalFeaturesBoolOption(const int key) const { return getBoolOption(key + ADDITIONAL_FEATURES_OPTIONS); } @@ -55,9 +61,10 @@ class SuggestOptions{ static const int USE_FULL_EDIT_DISTANCE = 1; static const int BLOCK_OFFENSIVE_WORDS = 2; static const int SPACE_AWARE_GESTURE_ENABLED = 3; + static const int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4; // Additional features options are stored after the other options and used as setting values of // experimental features. - static const int ADDITIONAL_FEATURES_OPTIONS = 4; + static const int ADDITIONAL_FEATURES_OPTIONS = 5; const int *const mOptions; const int mLength; diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp index 4c4dfc578..8fb256c54 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp @@ -38,15 +38,17 @@ const char *const HeaderPolicy::LOCALE_KEY = "locale"; // match Java declaration const char *const HeaderPolicy::FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY = "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID"; -const char *const HeaderPolicy::MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT"; -const char *const HeaderPolicy::MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT"; +const char *const HeaderPolicy::MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_ENTRY_COUNT"; +const char *const HeaderPolicy::MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_ENTRY_COUNT"; +const char *const HeaderPolicy::MAX_TRIGRAM_COUNT_KEY = "MAX_TRIGRAM_ENTRY_COUNT"; const int HeaderPolicy::DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE = 100; const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f; const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID = 3; const int HeaderPolicy::DEFAULT_MAX_UNIGRAM_COUNT = 10000; -const int HeaderPolicy::DEFAULT_MAX_BIGRAM_COUNT = 10000; +const int HeaderPolicy::DEFAULT_MAX_BIGRAM_COUNT = 30000; +const int HeaderPolicy::DEFAULT_MAX_TRIGRAM_COUNT = 30000; // Used for logging. Question mark is used to indicate that the key is not found. void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *outValue, diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h index bc8eaded3..836bbe5a1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -253,11 +253,13 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { static const char *const FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY; static const char *const MAX_UNIGRAM_COUNT_KEY; static const char *const MAX_BIGRAM_COUNT_KEY; + static const char *const MAX_TRIGRAM_COUNT_KEY; static const int DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE; static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE; static const int DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID; static const int DEFAULT_MAX_UNIGRAM_COUNT; static const int DEFAULT_MAX_BIGRAM_COUNT; + static const int DEFAULT_MAX_TRIGRAM_COUNT; const FormatUtils::FORMAT_VERSION mDictFormatVersion; const HeaderReadWriteUtils::DictionaryFlags mDictionaryFlags; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp index 8d169743c..6243f14cc 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp @@ -310,7 +310,7 @@ bool Ver4PatriciaTrieNodeWriter::addShortcutTarget(const PtNodeParams *const ptN const int shortcutProbability) { if (!mShortcutPolicy->addNewShortcut(ptNodeParams->getTerminalId(), targetCodePoints, targetCodePointCount, shortcutProbability)) { - AKLOGE("Cannot add new shortuct entry. terminalId: %d", ptNodeParams->getTerminalId()); + AKLOGE("Cannot add new shortcut entry. terminalId: %d", ptNodeParams->getTerminalId()); return false; } if (!ptNodeParams->hasShortcutTargets()) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp index 36eafa1e9..0eae934ae 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp @@ -33,7 +33,7 @@ #include "suggest/core/dictionary/property/ngram_property.h" #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" @@ -186,7 +186,9 @@ int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordI if (bigramsIt.getBigramPos() == ptNodePos && bigramsIt.getProbability() != NOT_A_PROBABILITY) { const int bigramConditionalProbability = getBigramConditionalProbability( - prevWordPtNodeParams.getProbability(), bigramsIt.getProbability()); + prevWordPtNodeParams.getProbability(), + prevWordPtNodeParams.representsBeginningOfSentence(), + bigramsIt.getProbability()); return getProbability(ptNodeParams.getProbability(), bigramConditionalProbability); } } @@ -209,15 +211,19 @@ void Ver4PatriciaTriePolicy::iterateNgramEntries(const WordIdArrayView prevWordI while (bigramsIt.hasNext()) { bigramsIt.next(); const int bigramConditionalProbability = getBigramConditionalProbability( - prevWordPtNodeParams.getProbability(), bigramsIt.getProbability()); + prevWordPtNodeParams.getProbability(), + prevWordPtNodeParams.representsBeginningOfSentence(), bigramsIt.getProbability()); listener->onVisitEntry(bigramConditionalProbability, getWordIdFromTerminalPtNodePos(bigramsIt.getBigramPos())); } } int Ver4PatriciaTriePolicy::getBigramConditionalProbability(const int prevWordUnigramProbability, - const int bigramProbability) const { + const bool isInBeginningOfSentenceContext, const int bigramProbability) const { if (mHeaderPolicy->hasHistoricalInfoOfWords()) { + if (isInBeginningOfSentenceContext) { + return bigramProbability; + } // Calculate conditional probability. return std::min(MAX_PROBABILITY - prevWordUnigramProbability + bigramProbability, MAX_PROBABILITY); @@ -338,7 +344,7 @@ bool Ver4PatriciaTriePolicy::removeUnigramEntry(const CodePointArrayView wordCod return mNodeWriter.suppressUnigramEntry(&ptNodeParams); } -bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, +bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); @@ -349,8 +355,8 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI mDictBuffer->getTailPosition()); return false; } - if (!prevWordsInfo->isValid()) { - AKLOGE("prev words info is not valid for adding n-gram entry to the dictionary."); + if (!ngramContext->isValid()) { + AKLOGE("Ngram context is not valid for adding n-gram entry to the dictionary."); return false; } if (ngramProperty->getTargetCodePoints()->size() > MAX_WORD_LENGTH) { @@ -359,23 +365,23 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI return false; } WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); if (prevWordIds.empty()) { return false; } if (prevWordIds[0] == NOT_A_WORD_ID) { - if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) { + if (ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */)) { const UnigramProperty beginningOfSentenceUnigramProperty( true /* representsBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */, MAX_PROBABILITY /* probability */, HistoricalInfo()); - if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */), + if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */), &beginningOfSentenceUnigramProperty)) { AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); return false; } // Refresh word ids. - prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); + ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); } else { return false; } @@ -399,7 +405,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI } } -bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, +bool Ver4PatriciaTriePolicy::removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: removeNgramEntry() is called for non-updatable dictionary."); @@ -410,8 +416,8 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor mDictBuffer->getTailPosition()); return false; } - if (!prevWordsInfo->isValid()) { - AKLOGE("prev words info is not valid for removing n-gram entry form the dictionary."); + if (!ngramContext->isValid()) { + AKLOGE("Ngram context is not valid for removing n-gram entry form the dictionary."); return false; } if (wordCodePoints.size() > MAX_WORD_LENGTH) { @@ -419,7 +425,7 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor wordCodePoints.size()); } WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSerch */); if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID) { return false; @@ -440,26 +446,27 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor } -bool Ver4PatriciaTriePolicy::updateCounter(const PrevWordsInfo *const prevWordsInfo, - const CodePointArrayView wordCodePoints, const bool isValidWord, - const HistoricalInfo historicalInfo) { +bool Ver4PatriciaTriePolicy::updateEntriesForWordWithNgramContext( + const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints, + const bool isValidWord, const HistoricalInfo historicalInfo) { if (!mBuffers->isUpdatable()) { - AKLOGI("Warning: updateCounter() is called for non-updatable dictionary."); + AKLOGI("Warning: updateEntriesForWordWithNgramContext() is called for non-updatable " + "dictionary."); return false; } const int probability = isValidWord ? DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY; const UnigramProperty unigramProperty(false /* representsBeginningOfSentence */, false /* isNotAWord */, false /*isBlacklisted*/, probability, historicalInfo); if (!addUnigramEntry(wordCodePoints, &unigramProperty)) { - AKLOGE("Cannot update unigarm entry in updateCounter()."); + AKLOGE("Cannot update unigarm entry in updateEntriesForWordWithNgramContext()."); return false; } - const int probabilityForNgram = prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */) + const int probabilityForNgram = ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */) ? NOT_A_PROBABILITY : probability; const NgramProperty ngramProperty(wordCodePoints.toVector(), probabilityForNgram, historicalInfo); - if (!addNgramEntry(prevWordsInfo, &ngramProperty)) { - AKLOGE("Cannot update unigarm entry in updateCounter()."); + if (!addNgramEntry(ngramContext, &ngramProperty)) { + AKLOGE("Cannot update unigarm entry in updateEntriesForWordWithNgramContext()."); return false; } return true; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h index b82563e61..1ad5e7e36 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h @@ -112,13 +112,13 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { bool removeUnigramEntry(const CodePointArrayView wordCodePoints); - bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty); - bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints); - bool updateCounter(const PrevWordsInfo *const prevWordsInfo, + bool updateEntriesForWordWithNgramContext(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints, const bool isValidWord, const HistoricalInfo historicalInfo); @@ -175,7 +175,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { const WordAttributes getWordAttributes(const int probability, const PtNodeParams &ptNodeParams) const; int getBigramConditionalProbability(const int prevWordUnigramProbability, - const int bigramProbability) const; + const bool isInBeginningOfSentenceContext, const int bigramProbability) const; }; } // namespace v402 } // namespace backward diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp index d3d684bfa..b7f1199c5 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp @@ -23,7 +23,7 @@ #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" #include "suggest/core/dictionary/multi_bigram_map.h" #include "suggest/core/dictionary/ngram_listener.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/probability_utils.h" diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h index 32a95bb6c..b17681388 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h @@ -93,25 +93,26 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { return false; } - bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); return false; } - bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: removeNgramEntry() is called for non-updatable dictionary."); return false; } - bool updateCounter(const PrevWordsInfo *const prevWordsInfo, + bool updateEntriesForWordWithNgramContext(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints, const bool isValidWord, const HistoricalInfo historicalInfo) { // This method should not be called for non-updatable dictionary. - AKLOGI("Warning: updateCounter() is called for non-updatable dictionary."); + AKLOGI("Warning: updateEntriesForWordWithNgramContext() is called for non-updatable " + "dictionary."); return false; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp index dc0ed96d0..90d4687dd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp @@ -37,7 +37,7 @@ const PtNodeParams Ver2ParticiaTrieNodeReader::fetchPtNodeParamsInBufferFromPtNo int shortcutPos = NOT_A_DICT_POS; int bigramPos = NOT_A_DICT_POS; int siblingPos = NOT_A_DICT_POS; - PatriciaTrieReadingUtils::readPtNodeInfo(mBuffer.data(), ptNodePos, mShortuctPolicy, + PatriciaTrieReadingUtils::readPtNodeInfo(mBuffer.data(), ptNodePos, mShortcutPolicy, mBigramPolicy, mCodePointTable, &flags, &mergedNodeCodePointCount, mergedNodeCodePoints, &probability, &childrenPos, &shortcutPos, &bigramPos, &siblingPos); if (mergedNodeCodePointCount <= 0) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h index 24ec5bcca..838d37314 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h @@ -35,7 +35,7 @@ class Ver2ParticiaTrieNodeReader : public PtNodeReader { const DictionaryBigramsStructurePolicy *const bigramPolicy, const DictionaryShortcutsStructurePolicy *const shortcutPolicy, const int *const codePointTable) - : mBuffer(buffer), mBigramPolicy(bigramPolicy), mShortuctPolicy(shortcutPolicy), + : mBuffer(buffer), mBigramPolicy(bigramPolicy), mShortcutPolicy(shortcutPolicy), mCodePointTable(codePointTable) {} virtual const PtNodeParams fetchPtNodeParamsInBufferFromPtNodePos(const int ptNodePos) const; @@ -45,7 +45,7 @@ class Ver2ParticiaTrieNodeReader : public PtNodeReader { const ReadOnlyByteArrayView mBuffer; const DictionaryBigramsStructurePolicy *const mBigramPolicy; - const DictionaryShortcutsStructurePolicy *const mShortuctPolicy; + const DictionaryShortcutsStructurePolicy *const mShortcutPolicy; const int *const mCodePointTable; }; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp index 956dabb4f..c4297f5d6 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp @@ -25,6 +25,7 @@ namespace latinime { const int LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE = 0; const int LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE = 1; +const int LanguageModelDictContent::DUMMY_PROBABILITY_FOR_VALID_WORDS = 1; bool LanguageModelDictContent::save(FILE *const file) const { return mTrieMap.save(file); @@ -42,18 +43,18 @@ const WordAttributes LanguageModelDictContent::getWordAttributes(const WordIdArr const int wordId, const HeaderPolicy *const headerPolicy) const { int bitmapEntryIndices[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; bitmapEntryIndices[0] = mTrieMap.getRootBitmapEntryIndex(); - int maxLevel = 0; + int maxPrevWordCount = 0; for (size_t i = 0; i < prevWordIds.size(); ++i) { const int nextBitmapEntryIndex = mTrieMap.get(prevWordIds[i], bitmapEntryIndices[i]).mNextLevelBitmapEntryIndex; if (nextBitmapEntryIndex == TrieMap::INVALID_INDEX) { break; } - maxLevel = i + 1; + maxPrevWordCount = i + 1; bitmapEntryIndices[i + 1] = nextBitmapEntryIndex; } - for (int i = maxLevel; i >= 0; --i) { + for (int i = maxPrevWordCount; i >= 0; --i) { const TrieMap::Result result = mTrieMap.get(wordId, bitmapEntryIndices[i]); if (!result.mIsValid) { continue; @@ -68,9 +69,24 @@ const WordAttributes LanguageModelDictContent::getWordAttributes(const WordIdArr // The entry should not be treated as a valid entry. continue; } - probability = std::min(rawProbability - + ForgettingCurveUtils::getProbabilityBiasForNgram(i + 1 /* n */), - MAX_PROBABILITY); + if (i == 0) { + // unigram + probability = rawProbability; + } else { + const ProbabilityEntry prevWordProbabilityEntry = getNgramProbabilityEntry( + prevWordIds.skip(1 /* n */).limit(i - 1), prevWordIds[0]); + if (!prevWordProbabilityEntry.isValid()) { + continue; + } + if (prevWordProbabilityEntry.representsBeginningOfSentence()) { + probability = rawProbability; + } else { + const int prevWordRawProbability = ForgettingCurveUtils::decodeProbability( + prevWordProbabilityEntry.getHistoricalInfo(), headerPolicy); + probability = std::min(MAX_PROBABILITY - prevWordRawProbability + + rawProbability, MAX_PROBABILITY); + } + } } else { probability = probabilityEntry.getProbability(); } @@ -143,6 +159,56 @@ bool LanguageModelDictContent::truncateEntries(const int *const entryCounts, return true; } +bool LanguageModelDictContent::updateAllEntriesOnInputWord(const WordIdArrayView prevWordIds, + const int wordId, const bool isValid, const HistoricalInfo historicalInfo, + const HeaderPolicy *const headerPolicy, int *const outAddedNewNgramEntryCount) { + if (outAddedNewNgramEntryCount) { + *outAddedNewNgramEntryCount = 0; + } + if (!mHasHistoricalInfo) { + AKLOGE("updateAllEntriesOnInputWord is called for dictionary without historical info."); + return false; + } + const ProbabilityEntry originalUnigramProbabilityEntry = getProbabilityEntry(wordId); + const ProbabilityEntry updatedUnigramProbabilityEntry = createUpdatedEntryFrom( + originalUnigramProbabilityEntry, isValid, historicalInfo, headerPolicy); + if (!setProbabilityEntry(wordId, &updatedUnigramProbabilityEntry)) { + return false; + } + for (size_t i = 0; i < prevWordIds.size(); ++i) { + if (prevWordIds[i] == NOT_A_WORD_ID) { + break; + } + // TODO: Optimize this code. + const WordIdArrayView limitedPrevWordIds = prevWordIds.limit(i + 1); + const ProbabilityEntry originalNgramProbabilityEntry = getNgramProbabilityEntry( + limitedPrevWordIds, wordId); + const ProbabilityEntry updatedNgramProbabilityEntry = createUpdatedEntryFrom( + originalNgramProbabilityEntry, isValid, historicalInfo, headerPolicy); + if (!setNgramProbabilityEntry(limitedPrevWordIds, wordId, &updatedNgramProbabilityEntry)) { + return false; + } + if (!originalNgramProbabilityEntry.isValid() && outAddedNewNgramEntryCount) { + *outAddedNewNgramEntryCount += 1; + } + } + return true; +} + +const ProbabilityEntry LanguageModelDictContent::createUpdatedEntryFrom( + const ProbabilityEntry &originalProbabilityEntry, const bool isValid, + const HistoricalInfo historicalInfo, const HeaderPolicy *const headerPolicy) const { + const HistoricalInfo updatedHistoricalInfo = ForgettingCurveUtils::createUpdatedHistoricalInfo( + originalProbabilityEntry.getHistoricalInfo(), isValid ? + DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY, + &historicalInfo, headerPolicy); + if (originalProbabilityEntry.isValid()) { + return ProbabilityEntry(originalProbabilityEntry.getFlags(), &updatedHistoricalInfo); + } else { + return ProbabilityEntry(0 /* flags */, &updatedHistoricalInfo); + } +} + bool LanguageModelDictContent::runGCInner( const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, const TrieMap::TrieMapRange trieMapRange, @@ -203,17 +269,27 @@ int LanguageModelDictContent::getBitmapEntryIndex(const WordIdArrayView prevWord return bitmapEntryIndex; } -bool LanguageModelDictContent::updateAllProbabilityEntriesInner(const int bitmapEntryIndex, - const int level, const HeaderPolicy *const headerPolicy, int *const outEntryCounts) { +bool LanguageModelDictContent::updateAllProbabilityEntriesForGCInner(const int bitmapEntryIndex, + const int prevWordCount, const HeaderPolicy *const headerPolicy, + int *const outEntryCounts) { for (const auto &entry : mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex)) { - if (level > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { - AKLOGE("Invalid level. level: %d, MAX_PREV_WORD_COUNT_FOR_N_GRAM: %d.", - level, MAX_PREV_WORD_COUNT_FOR_N_GRAM); + if (prevWordCount > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + AKLOGE("Invalid prevWordCount. prevWordCount: %d, MAX_PREV_WORD_COUNT_FOR_N_GRAM: %d.", + prevWordCount, MAX_PREV_WORD_COUNT_FOR_N_GRAM); return false; } const ProbabilityEntry probabilityEntry = ProbabilityEntry::decode(entry.value(), mHasHistoricalInfo); - if (mHasHistoricalInfo && !probabilityEntry.representsBeginningOfSentence()) { + if (prevWordCount > 0 && probabilityEntry.isValid() + && !mTrieMap.getRoot(entry.key()).mIsValid) { + // The entry is related to a word that has been removed. Remove the entry. + if (!mTrieMap.remove(entry.key(), bitmapEntryIndex)) { + return false; + } + continue; + } + if (mHasHistoricalInfo && !probabilityEntry.representsBeginningOfSentence() + && probabilityEntry.isValid()) { const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( probabilityEntry.getHistoricalInfo(), headerPolicy); if (ForgettingCurveUtils::needsToKeep(&historicalInfo, headerPolicy)) { @@ -232,13 +308,13 @@ bool LanguageModelDictContent::updateAllProbabilityEntriesInner(const int bitmap } } if (!probabilityEntry.representsBeginningOfSentence()) { - outEntryCounts[level] += 1; + outEntryCounts[prevWordCount] += 1; } if (!entry.hasNextLevelMap()) { continue; } - if (!updateAllProbabilityEntriesInner(entry.getNextLevelBitmapEntryIndex(), level + 1, - headerPolicy, outEntryCounts)) { + if (!updateAllProbabilityEntriesForGCInner(entry.getNextLevelBitmapEntryIndex(), + prevWordCount + 1, headerPolicy, outEntryCounts)) { return false; } } @@ -266,7 +342,7 @@ bool LanguageModelDictContent::turncateEntriesInSpecifiedLevel( for (int i = 0; i < entryCountToRemove; ++i) { const EntryInfoToTurncate &entryInfo = entryInfoVector[i]; if (!removeNgramProbabilityEntry( - WordIdArrayView(entryInfo.mPrevWordIds, entryInfo.mEntryLevel), entryInfo.mKey)) { + WordIdArrayView(entryInfo.mPrevWordIds, entryInfo.mPrevWordCount), entryInfo.mKey)) { return false; } } @@ -276,9 +352,9 @@ bool LanguageModelDictContent::turncateEntriesInSpecifiedLevel( bool LanguageModelDictContent::getEntryInfo(const HeaderPolicy *const headerPolicy, const int targetLevel, const int bitmapEntryIndex, std::vector<int> *const prevWordIds, std::vector<EntryInfoToTurncate> *const outEntryInfo) const { - const int currentLevel = prevWordIds->size(); + const int prevWordCount = prevWordIds->size(); for (const auto &entry : mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex)) { - if (currentLevel < targetLevel) { + if (prevWordCount < targetLevel) { if (!entry.hasNextLevelMap()) { continue; } @@ -313,10 +389,10 @@ bool LanguageModelDictContent::EntryInfoToTurncate::Comparator::operator()( if (left.mKey != right.mKey) { return left.mKey < right.mKey; } - if (left.mEntryLevel != right.mEntryLevel) { - return left.mEntryLevel > right.mEntryLevel; + if (left.mPrevWordCount != right.mPrevWordCount) { + return left.mPrevWordCount > right.mPrevWordCount; } - for (int i = 0; i < left.mEntryLevel; ++i) { + for (int i = 0; i < left.mPrevWordCount; ++i) { if (left.mPrevWordIds[i] != right.mPrevWordIds[i]) { return left.mPrevWordIds[i] < right.mPrevWordIds[i]; } @@ -326,9 +402,10 @@ bool LanguageModelDictContent::EntryInfoToTurncate::Comparator::operator()( } LanguageModelDictContent::EntryInfoToTurncate::EntryInfoToTurncate(const int probability, - const int timestamp, const int key, const int entryLevel, const int *const prevWordIds) - : mProbability(probability), mTimestamp(timestamp), mKey(key), mEntryLevel(entryLevel) { - memmove(mPrevWordIds, prevWordIds, mEntryLevel * sizeof(mPrevWordIds[0])); + const int timestamp, const int key, const int prevWordCount, const int *const prevWordIds) + : mProbability(probability), mTimestamp(timestamp), mKey(key), + mPrevWordCount(prevWordCount) { + memmove(mPrevWordIds, prevWordIds, mPrevWordCount * sizeof(mPrevWordIds[0])); } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h index b7e4af977..51ef090e1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h @@ -154,19 +154,23 @@ class LanguageModelDictContent { EntryRange getProbabilityEntries(const WordIdArrayView prevWordIds) const; - bool updateAllProbabilityEntries(const HeaderPolicy *const headerPolicy, + bool updateAllProbabilityEntriesForGC(const HeaderPolicy *const headerPolicy, int *const outEntryCounts) { for (int i = 0; i <= MAX_PREV_WORD_COUNT_FOR_N_GRAM; ++i) { outEntryCounts[i] = 0; } - return updateAllProbabilityEntriesInner(mTrieMap.getRootBitmapEntryIndex(), 0 /* level */, - headerPolicy, outEntryCounts); + return updateAllProbabilityEntriesForGCInner(mTrieMap.getRootBitmapEntryIndex(), + 0 /* prevWordCount */, headerPolicy, outEntryCounts); } // entryCounts should be created by updateAllProbabilityEntries. bool truncateEntries(const int *const entryCounts, const int *const maxEntryCounts, const HeaderPolicy *const headerPolicy, int *const outEntryCounts); + bool updateAllEntriesOnInputWord(const WordIdArrayView prevWordIds, const int wordId, + const bool isValid, const HistoricalInfo historicalInfo, + const HeaderPolicy *const headerPolicy, int *const outAddedNewNgramEntryCount); + private: DISALLOW_COPY_AND_ASSIGN(LanguageModelDictContent); @@ -181,18 +185,21 @@ class LanguageModelDictContent { }; EntryInfoToTurncate(const int probability, const int timestamp, const int key, - const int entryLevel, const int *const prevWordIds); + const int prevWordCount, const int *const prevWordIds); int mProbability; int mTimestamp; int mKey; - int mEntryLevel; + int mPrevWordCount; int mPrevWordIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; private: DISALLOW_DEFAULT_CONSTRUCTOR(EntryInfoToTurncate); }; + // TODO: Remove + static const int DUMMY_PROBABILITY_FOR_VALID_WORDS; + TrieMap mTrieMap; const bool mHasHistoricalInfo; @@ -201,13 +208,16 @@ class LanguageModelDictContent { int *const outNgramCount); int createAndGetBitmapEntryIndex(const WordIdArrayView prevWordIds); int getBitmapEntryIndex(const WordIdArrayView prevWordIds) const; - bool updateAllProbabilityEntriesInner(const int bitmapEntryIndex, const int level, + bool updateAllProbabilityEntriesForGCInner(const int bitmapEntryIndex, const int prevWordCount, const HeaderPolicy *const headerPolicy, int *const outEntryCounts); bool turncateEntriesInSpecifiedLevel(const HeaderPolicy *const headerPolicy, const int maxEntryCount, const int targetLevel, int *const outEntryCount); bool getEntryInfo(const HeaderPolicy *const headerPolicy, const int targetLevel, const int bitmapEntryIndex, std::vector<int> *const prevWordIds, std::vector<EntryInfoToTurncate> *const outEntryInfo) const; + const ProbabilityEntry createUpdatedEntryFrom(const ProbabilityEntry &originalProbabilityEntry, + const bool isValid, const HistoricalInfo historicalInfo, + const HeaderPolicy *const headerPolicy) const; }; } // namespace latinime #endif /* LATINIME_LANGUAGE_MODEL_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h index fa1415633..f4d340f86 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h @@ -98,17 +98,17 @@ class ProbabilityEntry { } uint64_t encode(const bool hasHistoricalInfo) const { - uint64_t encodedEntry = static_cast<uint64_t>(mFlags); + uint64_t encodedEntry = static_cast<uint8_t>(mFlags); if (hasHistoricalInfo) { encodedEntry = (encodedEntry << (Ver4DictConstants::TIME_STAMP_FIELD_SIZE * CHAR_BIT)) - ^ static_cast<uint64_t>(mHistoricalInfo.getTimestamp()); + | static_cast<uint32_t>(mHistoricalInfo.getTimestamp()); encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_LEVEL_FIELD_SIZE * CHAR_BIT)) - ^ static_cast<uint64_t>(mHistoricalInfo.getLevel()); + | static_cast<uint8_t>(mHistoricalInfo.getLevel()); encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) - ^ static_cast<uint64_t>(mHistoricalInfo.getCount()); + | static_cast<uint8_t>(mHistoricalInfo.getCount()); } else { encodedEntry = (encodedEntry << (Ver4DictConstants::PROBABILITY_SIZE * CHAR_BIT)) - ^ static_cast<uint64_t>(mProbability); + | static_cast<uint8_t>(mProbability); } return encodedEntry; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp index f13512d5a..794c63ffd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -142,14 +142,9 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeUnigramProperty( if (!toBeUpdatedPtNodeParams->isTerminal()) { return false; } - const ProbabilityEntry originalProbabilityEntry = - mBuffers->getLanguageModelDictContent()->getProbabilityEntry( - toBeUpdatedPtNodeParams->getTerminalId()); const ProbabilityEntry probabilityEntryOfUnigramProperty = ProbabilityEntry(unigramProperty); - const ProbabilityEntry updatedProbabilityEntry = - createUpdatedEntryFrom(&originalProbabilityEntry, &probabilityEntryOfUnigramProperty); return mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( - toBeUpdatedPtNodeParams->getTerminalId(), &updatedProbabilityEntry); + toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntryOfUnigramProperty); } bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( @@ -203,10 +198,8 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( // Write probability. ProbabilityEntry newProbabilityEntry; const ProbabilityEntry probabilityEntryOfUnigramProperty = ProbabilityEntry(unigramProperty); - const ProbabilityEntry probabilityEntryToWrite = createUpdatedEntryFrom( - &newProbabilityEntry, &probabilityEntryOfUnigramProperty); return mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( - terminalId, &probabilityEntryToWrite); + terminalId, &probabilityEntryOfUnigramProperty); } // TODO: Support counting ngram entries. @@ -217,10 +210,8 @@ bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds const ProbabilityEntry probabilityEntry = languageModelDictContent->getNgramProbabilityEntry(prevWordIds, wordId); const ProbabilityEntry probabilityEntryOfNgramProperty(ngramProperty); - const ProbabilityEntry updatedProbabilityEntry = createUpdatedEntryFrom( - &probabilityEntry, &probabilityEntryOfNgramProperty); if (!languageModelDictContent->setNgramProbabilityEntry( - prevWordIds, wordId, &updatedProbabilityEntry)) { + prevWordIds, wordId, &probabilityEntryOfNgramProperty)) { AKLOGE("Cannot add new ngram entry. prevWordId[0]: %d, prevWordId.size(): %zd, wordId: %d", prevWordIds[0], prevWordIds.size(), wordId); return false; @@ -285,7 +276,7 @@ bool Ver4PatriciaTrieNodeWriter::addShortcutTarget(const PtNodeParams *const ptN const int shortcutProbability) { if (!mShortcutPolicy->addNewShortcut(ptNodeParams->getTerminalId(), targetCodePoints, targetCodePointCount, shortcutProbability)) { - AKLOGE("Cannot add new shortuct entry. terminalId: %d", ptNodeParams->getTerminalId()); + AKLOGE("Cannot add new shortcut entry. terminalId: %d", ptNodeParams->getTerminalId()); return false; } return true; @@ -346,22 +337,6 @@ bool Ver4PatriciaTrieNodeWriter::writePtNodeAndGetTerminalIdAndAdvancePosition( ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); } -// TODO: Move probability handling code to LanguageModelDictContent. -const ProbabilityEntry Ver4PatriciaTrieNodeWriter::createUpdatedEntryFrom( - const ProbabilityEntry *const originalProbabilityEntry, - const ProbabilityEntry *const probabilityEntry) const { - if (mHeaderPolicy->hasHistoricalInfoOfWords()) { - const HistoricalInfo updatedHistoricalInfo = - ForgettingCurveUtils::createUpdatedHistoricalInfo( - originalProbabilityEntry->getHistoricalInfo(), - probabilityEntry->getProbability(), probabilityEntry->getHistoricalInfo(), - mHeaderPolicy); - return ProbabilityEntry(probabilityEntry->getFlags(), &updatedHistoricalInfo); - } else { - return *probabilityEntry; - } -} - bool Ver4PatriciaTrieNodeWriter::updatePtNodeFlags(const int ptNodePos, const bool isTerminal, const bool hasMultipleChars) { // Create node flags and write them. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h index ea4f09904..4ecf88729 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h @@ -38,11 +38,10 @@ class Ver4ShortcutListPolicy; class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { public: Ver4PatriciaTrieNodeWriter(BufferWithExtendableBuffer *const trieBuffer, - Ver4DictBuffers *const buffers, const HeaderPolicy *const headerPolicy, - const PtNodeReader *const ptNodeReader, + Ver4DictBuffers *const buffers, const PtNodeReader *const ptNodeReader, const PtNodeArrayReader *const ptNodeArrayReader, Ver4ShortcutListPolicy *const shortcutPolicy) - : mTrieBuffer(trieBuffer), mBuffers(buffers), mHeaderPolicy(headerPolicy), + : mTrieBuffer(trieBuffer), mBuffers(buffers), mReadingHelper(ptNodeReader, ptNodeArrayReader), mShortcutPolicy(shortcutPolicy) {} virtual ~Ver4PatriciaTrieNodeWriter() {} @@ -96,20 +95,12 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { const PtNodeParams *const ptNodeParams, int *const outTerminalId, int *const ptNodeWritingPos); - // Create updated probability entry using given probability property. In addition to the - // probability, this method updates historical information if needed. - // TODO: Update flags. - const ProbabilityEntry createUpdatedEntryFrom( - const ProbabilityEntry *const originalProbabilityEntry, - const ProbabilityEntry *const probabilityEntry) const; - bool updatePtNodeFlags(const int ptNodePos, const bool isTerminal, const bool hasMultipleChars); static const int CHILDREN_POSITION_FIELD_SIZE; BufferWithExtendableBuffer *const mTrieBuffer; Ver4DictBuffers *const mBuffers; - const HeaderPolicy *const mHeaderPolicy; DynamicPtReadingHelper mReadingHelper; Ver4ShortcutListPolicy *const mShortcutPolicy; }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp index 036197c41..ea8c0dc22 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -26,7 +26,7 @@ #include "suggest/core/dictionary/property/ngram_property.h" #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" @@ -43,7 +43,6 @@ const char *const Ver4PatriciaTriePolicy::MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_C const int Ver4PatriciaTriePolicy::MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS = 1024; const int Ver4PatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS = Ver4DictConstants::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; -const int Ver4PatriciaTriePolicy::DUMMY_PROBABILITY_FOR_VALID_WORDS = 1; void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode, DicNodeVector *const childDicNodes) const { @@ -151,8 +150,7 @@ void Ver4PatriciaTriePolicy::iterateNgramEntries(const WordIdArrayView prevWordI } const int probability = probabilityEntry.hasHistoricalInfo() ? ForgettingCurveUtils::decodeProbability( - probabilityEntry.getHistoricalInfo(), mHeaderPolicy) - + ForgettingCurveUtils::getProbabilityBiasForNgram(i + 1 /* n */) : + probabilityEntry.getHistoricalInfo(), mHeaderPolicy) : probabilityEntry.getProbability(); listener->onVisitEntry(probability, entry.getWordId()); } @@ -266,7 +264,7 @@ bool Ver4PatriciaTriePolicy::removeUnigramEntry(const CodePointArrayView wordCod return true; } -bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, +bool Ver4PatriciaTriePolicy::addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); @@ -277,8 +275,8 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI mDictBuffer->getTailPosition()); return false; } - if (!prevWordsInfo->isValid()) { - AKLOGE("prev words info is not valid for adding n-gram entry to the dictionary."); + if (!ngramContext->isValid()) { + AKLOGE("Ngram context is not valid for adding n-gram entry to the dictionary."); return false; } if (ngramProperty->getTargetCodePoints()->size() > MAX_WORD_LENGTH) { @@ -287,7 +285,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI return false; } WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); if (prevWordIds.empty()) { return false; @@ -296,19 +294,19 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI if (prevWordIds[i] != NOT_A_WORD_ID) { continue; } - if (!prevWordsInfo->isNthPrevWordBeginningOfSentence(i + 1 /* n */)) { + if (!ngramContext->isNthPrevWordBeginningOfSentence(i + 1 /* n */)) { return false; } const UnigramProperty beginningOfSentenceUnigramProperty( true /* representsBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */, MAX_PROBABILITY /* probability */, HistoricalInfo()); - if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */), + if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */), &beginningOfSentenceUnigramProperty)) { AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); return false; } // Refresh word ids. - prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); + ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); } const int wordId = getWordId(CodePointArrayView(*ngramProperty->getTargetCodePoints()), false /* forceLowerCaseSearch */); @@ -326,7 +324,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI } } -bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, +bool Ver4PatriciaTriePolicy::removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: removeNgramEntry() is called for non-updatable dictionary."); @@ -337,8 +335,8 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor mDictBuffer->getTailPosition()); return false; } - if (!prevWordsInfo->isValid()) { - AKLOGE("prev words info is not valid for removing n-gram entry form the dictionary."); + if (!ngramContext->isValid()) { + AKLOGE("Ngram context is not valid for removing n-gram entry form the dictionary."); return false; } if (wordCodePoints.size() > MAX_WORD_LENGTH) { @@ -346,7 +344,7 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor wordCodePoints.size()); } WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; - const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSerch */); if (prevWordIds.empty() || prevWordIds.contains(NOT_A_WORD_ID)) { return false; @@ -363,32 +361,52 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor } } -bool Ver4PatriciaTriePolicy::updateCounter(const PrevWordsInfo *const prevWordsInfo, - const CodePointArrayView wordCodePoints, const bool isValidWord, - const HistoricalInfo historicalInfo) { +bool Ver4PatriciaTriePolicy::updateEntriesForWordWithNgramContext( + const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints, + const bool isValidWord, const HistoricalInfo historicalInfo) { if (!mBuffers->isUpdatable()) { - AKLOGI("Warning: updateCounter() is called for non-updatable dictionary."); + AKLOGI("Warning: updateEntriesForWordWithNgramContext() is called for non-updatable " + "dictionary."); return false; } - // TODO: Have count up method in language model dict content. - const int probability = isValidWord ? DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY; - const UnigramProperty unigramProperty(false /* representsBeginningOfSentence */, - false /* isNotAWord */, false /*isBlacklisted*/, probability, historicalInfo); - if (!addUnigramEntry(wordCodePoints, &unigramProperty)) { - AKLOGE("Cannot update unigarm entry in updateCounter()."); - return false; + const bool updateAsAValidWord = ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */) ? + false : isValidWord; + int wordId = getWordId(wordCodePoints, false /* tryLowerCaseSearch */); + if (wordId == NOT_A_WORD_ID) { + // The word is not in the dictionary. + const UnigramProperty unigramProperty(false /* representsBeginningOfSentence */, + false /* isNotAWord */, false /* isBlacklisted */, NOT_A_PROBABILITY, + HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */)); + if (!addUnigramEntry(wordCodePoints, &unigramProperty)) { + AKLOGE("Cannot add unigarm entry in updateEntriesForWordWithNgramContext()."); + return false; + } + wordId = getWordId(wordCodePoints, false /* tryLowerCaseSearch */); } - const int probabilityForNgram = prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */) - ? NOT_A_PROBABILITY : probability; - const NgramProperty ngramProperty(wordCodePoints.toVector(), probabilityForNgram, - historicalInfo); - for (size_t i = 1; i <= prevWordsInfo->getPrevWordCount(); ++i) { - const PrevWordsInfo trimmedPrevWordsInfo(prevWordsInfo->getTrimmedPrevWordsInfo(i)); - if (!addNgramEntry(&trimmedPrevWordsInfo, &ngramProperty)) { - AKLOGE("Cannot update ngram entry in updateCounter()."); + + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = ngramContext->getPrevWordIds(this, &prevWordIdArray, + false /* tryLowerCaseSearch */); + if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID + && ngramContext->isNthPrevWordBeginningOfSentence(1 /* n */)) { + const UnigramProperty beginningOfSentenceUnigramProperty( + true /* representsBeginningOfSentence */, + true /* isNotAWord */, false /* isBlacklisted */, NOT_A_PROBABILITY, + HistoricalInfo(historicalInfo.getTimestamp(), 0 /* level */, 0 /* count */)); + if (!addUnigramEntry(ngramContext->getNthPrevWordCodePoints(1 /* n */), + &beginningOfSentenceUnigramProperty)) { + AKLOGE("Cannot add BoS entry in updateEntriesForWordWithNgramContext()."); return false; } + // Refresh word ids. + ngramContext->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); + } + int addedNewNgramEntryCount = 0; + if (!mBuffers->getMutableLanguageModelDictContent()->updateAllEntriesOnInputWord(prevWordIds, + wordId, updateAsAValidWord, historicalInfo, mHeaderPolicy, &addedNewNgramEntryCount)) { + return false; } + mBigramCount += addedNewNgramEntryCount; return true; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h index 662bb8d4b..c0532815c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h @@ -47,8 +47,8 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { mShortcutPolicy(mBuffers->getMutableShortcutDictContent(), mBuffers->getTerminalPositionLookupTable()), mNodeReader(mDictBuffer), mPtNodeArrayReader(mDictBuffer), - mNodeWriter(mDictBuffer, mBuffers.get(), mHeaderPolicy, &mNodeReader, - &mPtNodeArrayReader, &mShortcutPolicy), + mNodeWriter(mDictBuffer, mBuffers.get(), &mNodeReader, &mPtNodeArrayReader, + &mShortcutPolicy), mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter), mWritingHelper(mBuffers.get()), mUnigramCount(mHeaderPolicy->getUnigramCount()), @@ -92,13 +92,13 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { bool removeUnigramEntry(const CodePointArrayView wordCodePoints); - bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool addNgramEntry(const NgramContext *const ngramContext, const NgramProperty *const ngramProperty); - bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + bool removeNgramEntry(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints); - bool updateCounter(const PrevWordsInfo *const prevWordsInfo, + bool updateEntriesForWordWithNgramContext(const NgramContext *const ngramContext, const CodePointArrayView wordCodePoints, const bool isValidWord, const HistoricalInfo historicalInfo); @@ -131,8 +131,6 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { // prevent the dictionary from overflowing. static const int MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS; - // TODO: Remove - static const int DUMMY_PROBABILITY_FOR_VALID_WORDS; const Ver4DictBuffers::Ver4DictBuffersPtr mBuffers; const HeaderPolicy *const mHeaderPolicy; @@ -144,6 +142,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { DynamicPtUpdatingHelper mUpdatingHelper; Ver4PatriciaTrieWritingHelper mWritingHelper; int mUnigramCount; + // TODO: Support counting ngram entries. int mBigramCount; std::vector<int> mTerminalPtNodePositionsForIteratingWords; mutable bool mIsCorrupted; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp index e1ff973de..f0d59c150 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp @@ -78,11 +78,11 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(), mBuffers->getTerminalPositionLookupTable()); Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(), - mBuffers, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy); + mBuffers, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy); int entryCountTable[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; - if (!mBuffers->getMutableLanguageModelDictContent()->updateAllProbabilityEntries(headerPolicy, - entryCountTable)) { + if (!mBuffers->getMutableLanguageModelDictContent()->updateAllProbabilityEntriesForGC( + headerPolicy, entryCountTable)) { AKLOGE("Failed to update probabilities in language model dict content."); return false; } @@ -118,7 +118,7 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, PtNodeWriter::DictPositionRelocationMap dictPositionRelocationMap; readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); Ver4PatriciaTrieNodeWriter ptNodeWriterForNewBuffers(buffersToWrite->getWritableTrieBuffer(), - buffersToWrite, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy); + buffersToWrite, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy); DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer traversePolicyToPlaceAndWriteValidPtNodesToBuffer(&ptNodeWriterForNewBuffers, buffersToWrite->getWritableTrieBuffer(), &dictPositionRelocationMap); @@ -133,7 +133,7 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, Ver4ShortcutListPolicy newShortcutPolicy(buffersToWrite->getMutableShortcutDictContent(), buffersToWrite->getTerminalPositionLookupTable()); Ver4PatriciaTrieNodeWriter newPtNodeWriter(buffersToWrite->getWritableTrieBuffer(), - buffersToWrite, headerPolicy, &newPtNodeReader, &newPtNodeArrayreader, + buffersToWrite, &newPtNodeReader, &newPtNodeArrayreader, &newShortcutPolicy); // Re-assign terminal IDs for valid terminal PtNodes. TerminalPositionLookupTable::TerminalIdMap terminalIdMap; diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp index 3fc566e7a..6a2db687d 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp @@ -31,6 +31,7 @@ const float ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH = 0.03f; // TODO: Unlimit max cache dic node size const int ScoringParams::MAX_CACHE_DIC_NODE_SIZE = 170; const int ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT = 310; +const int ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_LOW_PROBABILITY_LOCALE = 50; const int ScoringParams::THRESHOLD_SHORT_WORD_LENGTH = 4; const float ScoringParams::DISTANCE_WEIGHT_LENGTH = 0.1524f; @@ -48,7 +49,7 @@ const float ScoringParams::INSERTION_COST_PROXIMITY_CHAR = 0.674f; const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.639f; const float ScoringParams::TRANSPOSITION_COST = 0.5608f; const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.334f; -const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.4576f; +const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.37972f; const float ScoringParams::SUBSTITUTION_COST = 0.3806f; const float ScoringParams::COST_NEW_WORD = 0.0314f; const float ScoringParams::COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE = 0.3224f; @@ -61,4 +62,7 @@ const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.4182f; const float ScoringParams::TYPING_BASE_OUTPUT_SCORE = 1.0f; const float ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT = 0.1f; const float ScoringParams::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT = 0.095f; +const float ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_SUBSTITUTION = 0.99f; +const float ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_OMISSION = 0.99f; +const float ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SMALL_CACHE_SIZE = 0.99f; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h index b12de6d87..731424f3d 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h @@ -30,6 +30,7 @@ class ScoringParams { static const float AUTOCORRECT_OUTPUT_THRESHOLD; static const int MAX_CACHE_DIC_NODE_SIZE; static const int MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT; + static const int MAX_CACHE_DIC_NODE_SIZE_FOR_LOW_PROBABILITY_LOCALE; static const int THRESHOLD_SHORT_WORD_LENGTH; static const float EXACT_MATCH_PROMOTION; @@ -68,6 +69,9 @@ class ScoringParams { static const float TYPING_BASE_OUTPUT_SCORE; static const float TYPING_MAX_OUTPUT_SCORE_PER_INPUT; static const float NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT; + static const float LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_SUBSTITUTION; + static const float LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_OMISSION; + static const float LOCALE_WEIGHT_THRESHOLD_FOR_SMALL_CACHE_SIZE; private: DISALLOW_IMPLICIT_CONSTRUCTORS(ScoringParams); diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index b64ee8be4..b9b6314ae 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -26,6 +26,7 @@ #include "suggest/core/layout/proximity_info_utils.h" #include "suggest/core/policy/traversal.h" #include "suggest/core/session/dic_traverse_session.h" +#include "suggest/core/suggest_options.h" #include "suggest/policyimpl/typing/scoring_params.h" #include "utils/char_utils.h" @@ -77,6 +78,13 @@ class TypingTraversal : public Traversal { if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) { return false; } + if (traverseSession->getSuggestOptions()->weightForLocale() + < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_SUBSTITUTION) { + // Space substitution is heavy, so we skip doing it if the weight for this language + // is low because we anticipate the suggestions out of this dictionary are not for + // the language the user intends to type in. + return false; + } if (!canDoLookAheadCorrection(traverseSession, dicNode)) { return false; } @@ -91,6 +99,13 @@ class TypingTraversal : public Traversal { if (!CORRECT_NEW_WORD_SPACE_OMISSION) { return false; } + if (traverseSession->getSuggestOptions()->weightForLocale() + < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_OMISSION) { + // Space omission is heavy, so we skip doing it if the weight for this language + // is low because we anticipate the suggestions out of this dictionary are not for + // the language the user intends to type in. + return false; + } const int inputSize = traverseSession->getInputSize(); // TODO: Don't refer to isCompletion? if (dicNode->isCompletion(inputSize)) { @@ -141,9 +156,14 @@ class TypingTraversal : public Traversal { return DicNodeVector::DEFAULT_NODES_SIZE_FOR_OPTIMIZATION; } - AK_FORCE_INLINE int getMaxCacheSize(const int inputSize) const { - return (inputSize <= 1) ? ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT - : ScoringParams::MAX_CACHE_DIC_NODE_SIZE; + AK_FORCE_INLINE int getMaxCacheSize(const int inputSize, const float weightForLocale) const { + if (inputSize <= 1) { + return ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT; + } + if (weightForLocale < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SMALL_CACHE_SIZE) { + return ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_LOW_PROBABILITY_LOCALE; + } + return ScoringParams::MAX_CACHE_DIC_NODE_SIZE; } AK_FORCE_INLINE int getTerminalCacheSize() const { diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp index 1d590c353..db7a39efb 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp @@ -68,7 +68,8 @@ ErrorTypeUtils::ErrorType TypingWeighting::getErrorType(const CorrectionType cor } break; case CT_ADDITIONAL_PROXIMITY: - return ErrorTypeUtils::PROXIMITY_CORRECTION; + // TODO: Change to EDIT_CORRECTION. + return ErrorTypeUtils::PROXIMITY_CORRECTION; case CT_OMISSION: if (parentDicNode->canBeIntentionalOmission()) { return ErrorTypeUtils::INTENTIONAL_OMISSION; @@ -77,6 +78,8 @@ ErrorTypeUtils::ErrorType TypingWeighting::getErrorType(const CorrectionType cor } break; case CT_SUBSTITUTION: + // TODO: Quit settng PROXIMITY_CORRECTION. + return ErrorTypeUtils::EDIT_CORRECTION | ErrorTypeUtils::PROXIMITY_CORRECTION; case CT_INSERTION: case CT_TERMINAL_INSERTION: case CT_TRANSPOSITION: diff --git a/native/jni/src/utils/jni_data_utils.h b/native/jni/src/utils/jni_data_utils.h index 235a03bba..25cc41742 100644 --- a/native/jni/src/utils/jni_data_utils.h +++ b/native/jni/src/utils/jni_data_utils.h @@ -21,7 +21,7 @@ #include "defines.h" #include "jni.h" -#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/session/ngram_context.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" #include "utils/char_utils.h" @@ -96,7 +96,7 @@ class JniDataUtils { } } - static PrevWordsInfo constructPrevWordsInfo(JNIEnv *env, jobjectArray prevWordCodePointArrays, + static NgramContext constructNgramContext(JNIEnv *env, jobjectArray prevWordCodePointArrays, jbooleanArray isBeginningOfSentenceArray, const size_t prevWordCount) { int prevWordCodePoints[MAX_PREV_WORD_COUNT_FOR_N_GRAM][MAX_WORD_LENGTH]; int prevWordCodePointCount[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; @@ -119,7 +119,7 @@ class JniDataUtils { &isBeginningOfSentenceBoolean); isBeginningOfSentence[i] = isBeginningOfSentenceBoolean == JNI_TRUE; } - return PrevWordsInfo(prevWordCodePoints, prevWordCodePointCount, isBeginningOfSentence, + return NgramContext(prevWordCodePoints, prevWordCodePointCount, isBeginningOfSentence, prevWordCount); } diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 26195d39f..c7a9e13a2 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -21,7 +21,8 @@ <uses-permission android:name="android.permission.READ_CONTACTS" /> - <application> + <application android:label="@string/app_name" + android:icon="@drawable/ic_app"> <uses-library android:name="android.test.runner" /> <!-- meta-data android:name="com.android.contacts.iconset" android:resource="@xml/iconset" /--> </application> diff --git a/tests/res/drawable-hdpi/ic_app.png b/tests/res/drawable-hdpi/ic_app.png Binary files differnew file mode 100644 index 000000000..345c23d2c --- /dev/null +++ b/tests/res/drawable-hdpi/ic_app.png diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml new file mode 100644 index 000000000..5cc48b6bf --- /dev/null +++ b/tests/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> +<resources> + <string name="app_name" translatable="false">LatinIMETests</string> +</resources> diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java index 34cf4072f..d642a1073 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java @@ -348,6 +348,31 @@ public class KeyboardThemeTests extends AndroidTestCase { } /* + * Test that KeyboardTheme array should be sorted by descending order of + * {@link KeyboardTheme#mMinApiVersion}. + */ + private static void assertSortedKeyboardThemeArray(final KeyboardTheme[] array) { + assertNotNull(array); + final int length = array.length; + assertTrue("array length=" + length, length > 0); + for (int index = 0; index < length - 1; index++) { + final KeyboardTheme theme = array[index]; + final KeyboardTheme nextTheme = array[index + 1]; + assertTrue("sorted MinApiVersion: " + + theme.mThemeName + ": minApiVersion=" + theme.mMinApiVersion, + theme.mMinApiVersion >= nextTheme.mMinApiVersion); + } + } + + public void testSortedKeyboardTheme() { + assertSortedKeyboardThemeArray(KeyboardTheme.KEYBOARD_THEMES); + } + + public void testSortedAvailableKeyboardTheme() { + assertSortedKeyboardThemeArray(KeyboardTheme.getAvailableThemeArray(getContext())); + } + + /* * Test for missing selected theme. */ private static KeyboardTheme[] LIMITED_THEMES = { @@ -356,6 +381,7 @@ public class KeyboardThemeTests extends AndroidTestCase { }; static { Arrays.sort(LIMITED_THEMES); + assertSortedKeyboardThemeArray(LIMITED_THEMES); } public void testMissingSelectedThemeIcs() { diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java index 84c5df622..4f84c6806 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java @@ -140,8 +140,8 @@ public final class Telugu extends LayoutBase { key("\u0C2A", moreKey("\u0C2B")), // U+0C30: "ర" TELUGU LETTER RA // U+0C31: "ఱ" TELUGU LETTER RRA - // U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R - key("\u0C30", joinMoreKeys("\u0C31", "\u0C43")), + // U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA + key("\u0C30", joinMoreKeys("\u0C31", "\u0C4D\u0C30")), // U+0C15: "క" TELUGU LETTER KA // U+0C16: "ఖ" TELUGU LETTER KHA key("\u0C15", moreKey("\u0C16")), @@ -155,18 +155,21 @@ public final class Telugu extends LayoutBase { // U+0C20: "ఠ" TELUGU LETTER TTHA key("\u0C1F", moreKey("\u0C20"))) .setKeysOfRow(3, - // U+0C46: "ె" TELUGU VOWEL SIGN E + // U+0C4A: "ొ" TELUGU VOWEL SIGN O // U+0C12: "ఒ" TELUGU LETTER O - key("\u0C46", moreKey("\u0C12")), - // U+0C02: "ం" TELUGU SIGN ANUSVARA + key("\u0C4A", moreKey("\u0C12")), + // U+0C46: "ె" TELUGU VOWEL SIGN E // U+0C0E: "ఎ" TELUGU LETTER E - key("\u0C02", moreKey("\u0C0E")), + key("\u0C46", moreKey("\u0C0E")), // U+0C2E: "మ" TELUGU LETTER MA - "\u0C2E", + // U+0C02: "ం" TELUGU SIGN ANUSVARA + // U+0C01: "ఁ" TELUGU SIGN CANDRABINDU + key("\u0C2E", joinMoreKeys("\u0C02", "\u0C01")), // U+0C28: "న" TELUGU LETTER NA // U+0C23: "ణ" TELUGU LETTER NNA // U+0C19: "ఙ" TELUGU LETTER NGA - key("\u0C28", joinMoreKeys("\u0C23", "\u0C19")), + // U+0C1E: "ఞ" TELUGU LETTER NYA + key("\u0C28", joinMoreKeys("\u0C23", "\u0C19", "\u0C1E")), // U+0C35: "వ" TELUGU LETTER VA "\u0C35", // U+0C32: "ల" TELUGU LETTER LA @@ -176,8 +179,8 @@ public final class Telugu extends LayoutBase { // U+0C36: "శ" TELUGU LETTER SHA key("\u0C38", moreKey("\u0C36")), // U+0C0B: "ఋ" TELUGU LETTER VOCALIC R - // U+0C4D/U+0C30: "్ర" TELUGU SIGN VIRAMA/TELUGU LETTER RA - key("\u0C0B", moreKey("\u0C4D\u0C30")), + // U+0C43: "ృ" TELUGU VOWEL SIGN VOCALIC R + key("\u0C0B", moreKey("\u0C43")), // U+0C37: "ష" TELUGU LETTER SSA // U+0C15/U+0C4D/U+0C37: // "క్ష" TELUGU LETTER KA/TELUGU SIGN VIRAMA/TELUGU LETTER SSA diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index 0e58b7211..f9ae9b8e4 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -39,7 +39,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; -import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -75,6 +74,10 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { return formatVersion > FormatSpec.VERSION401; } + private static boolean supportsNgram(final int formatVersion) { + return formatVersion >= FormatSpec.VERSION4_DEV; + } + private void onInputWord(final BinaryDictionary binaryDictionary, final String word, final boolean isValidWord) { binaryDictionary.updateEntriesForWordWithNgramContext(NgramContext.EMPTY_PREV_WORDS_INFO, @@ -88,6 +91,14 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { mCurrentTime /* timestamp */); } + private void onInputWordWithPrevWords(final BinaryDictionary binaryDictionary, + final String word, final boolean isValidWord, final String prevWord, + final String prevPrevWord) { + binaryDictionary.updateEntriesForWordWithNgramContext( + new NgramContext(new WordInfo(prevWord), new WordInfo(prevPrevWord)), word, + isValidWord, 1 /* count */, mCurrentTime /* timestamp */); + } + private void onInputWordWithBeginningOfSentenceContext( final BinaryDictionary binaryDictionary, final String word, final boolean isValidWord) { binaryDictionary.updateEntriesForWordWithNgramContext(NgramContext.BEGINNING_OF_SENTENCE, @@ -99,6 +110,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { return binaryDictionary.isValidNgram(new NgramContext(new WordInfo(word0)), word1); } + private static boolean isValidTrigram(final BinaryDictionary binaryDictionary, + final String word0, final String word1, final String word2) { + return binaryDictionary.isValidNgram( + new NgramContext(new WordInfo(word1), new WordInfo(word0)), word2); + } + private void forcePassingShortTime(final BinaryDictionary binaryDictionary) { // 30 days. final int timeToElapse = (int)TimeUnit.SECONDS.convert(30, TimeUnit.DAYS); @@ -118,11 +135,18 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private HashSet<File> mDictFilesToBeDeleted = new HashSet<>(); private File createEmptyDictionaryAndGetFile(final int formatVersion) { + return createEmptyDictionaryWithAttributeMapAndGetFile(formatVersion, + new HashMap<String, String>()); + } + + private File createEmptyDictionaryWithAttributeMapAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) { if (formatVersion == FormatSpec.VERSION4 || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING || formatVersion == FormatSpec.VERSION4_DEV) { try { - final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion, + attributeMap); mDictFilesToBeDeleted.add(dictFile); return dictFile; } catch (final IOException e) { @@ -134,12 +158,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { return null; } - private File createEmptyVer4DictionaryAndGetFile(final int formatVersion) + private File createEmptyVer4DictionaryAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) throws IOException { final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); FileUtils.deleteRecursively(file); - Map<String, String> attributeMap = new HashMap<>(); attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, DICTIONARY_ID); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); @@ -256,7 +280,23 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { onInputWordWithPrevWord(binaryDictionary, "y", true /* isValidWord */, "x"); assertFalse(isValidBigram(binaryDictionary, "x", "y")); - binaryDictionary.close(); + if (!supportsNgram(formatVersion)) { + return; + } + + onInputWordWithPrevWords(binaryDictionary, "c", false /* isValidWord */, "b", "a"); + assertFalse(isValidTrigram(binaryDictionary, "a", "b", "c")); + assertFalse(isValidBigram(binaryDictionary, "b", "c")); + onInputWordWithPrevWords(binaryDictionary, "c", false /* isValidWord */, "b", "a"); + assertTrue(isValidTrigram(binaryDictionary, "a", "b", "c")); + assertTrue(isValidBigram(binaryDictionary, "b", "c")); + + onInputWordWithPrevWords(binaryDictionary, "d", true /* isValidWord */, "b", "a"); + assertTrue(isValidTrigram(binaryDictionary, "a", "b", "d")); + assertTrue(isValidBigram(binaryDictionary, "b", "d")); + + onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "b", "a"); + assertTrue(isValidTrigram(binaryDictionary, "a", "b", "cd")); } public void testDecayingProbability() { @@ -301,6 +341,31 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { forcePassingLongTime(binaryDictionary); assertFalse(isValidBigram(binaryDictionary, "a", "b")); + if (!supportsNgram(formatVersion)) { + return; + } + + onInputWord(binaryDictionary, "ab", true /* isValidWord */); + onInputWordWithPrevWord(binaryDictionary, "bc", true /* isValidWord */, "ab"); + onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "bc", "ab"); + assertTrue(isValidTrigram(binaryDictionary, "ab", "bc", "cd")); + forcePassingShortTime(binaryDictionary); + assertFalse(isValidTrigram(binaryDictionary, "ab", "bc", "cd")); + + onInputWord(binaryDictionary, "ab", true /* isValidWord */); + onInputWordWithPrevWord(binaryDictionary, "bc", true /* isValidWord */, "ab"); + onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "bc", "ab"); + onInputWord(binaryDictionary, "ab", true /* isValidWord */); + onInputWordWithPrevWord(binaryDictionary, "bc", true /* isValidWord */, "ab"); + onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "bc", "ab"); + onInputWord(binaryDictionary, "ab", true /* isValidWord */); + onInputWordWithPrevWord(binaryDictionary, "bc", true /* isValidWord */, "ab"); + onInputWordWithPrevWords(binaryDictionary, "cd", true /* isValidWord */, "bc", "ab"); + forcePassingShortTime(binaryDictionary); + assertTrue(isValidTrigram(binaryDictionary, "ab", "bc", "cd")); + forcePassingLongTime(binaryDictionary); + assertFalse(isValidTrigram(binaryDictionary, "ab", "bc", "cd")); + binaryDictionary.close(); } @@ -329,7 +394,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } final int maxUnigramCount = Integer.parseInt( - binaryDictionary.getPropertyForGettingStats(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); + binaryDictionary.getPropertyForGettingStats( + BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); for (int i = 0; i < unigramTypedCount; i++) { final String word = words.get(random.nextInt(words.size())); onInputWord(binaryDictionary, word, true /* isValidWord */); @@ -417,6 +483,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } private void testAddManyBigramsToDecayingDict(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int unigramCount = 5000; final int bigramCount = 30000; final int bigramTypedCount = 100000; @@ -425,7 +497,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { final Random random = new Random(seed); setCurrentTimeForTestMode(mCurrentTime); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributeMapAndGetFile(formatVersion, + attributeMap); final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); @@ -448,9 +521,6 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { bigrams.add(bigram); } - final int maxBigramCount = Integer.parseInt( - binaryDictionary.getPropertyForGettingStats( - BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); for (int i = 0; i < bigramTypedCount; ++i) { final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size())); onInputWord(binaryDictionary, bigram.first, true /* isValidWord */); @@ -487,6 +557,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } private void testOverflowBigrams(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int bigramCount = 20000; final int unigramCount = 1000; final int unigramTypedCount = 20; @@ -497,7 +573,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { final long seed = System.currentTimeMillis(); final Random random = new Random(seed); setCurrentTimeForTestMode(mCurrentTime); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributeMapAndGetFile(formatVersion, + attributeMap); final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 90dd4366c..a640a9835 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -23,6 +23,7 @@ import android.util.Pair; import com.android.inputmethod.latin.NgramContext.WordInfo; import com.android.inputmethod.latin.makedict.CodePointUtils; +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; @@ -78,11 +79,18 @@ public class BinaryDictionaryTests extends AndroidTestCase { } private File createEmptyDictionaryAndGetFile(final int formatVersion) { + return createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + new HashMap<String, String>()); + } + + private File createEmptyDictionaryWithAttributesAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) { if (formatVersion == FormatSpec.VERSION4 || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING || formatVersion == FormatSpec.VERSION4_DEV) { try { - final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion, + attributeMap); mDictFilesToBeDeleted.add(dictFile); return dictFile; } catch (final IOException e) { @@ -94,12 +102,12 @@ public class BinaryDictionaryTests extends AndroidTestCase { return null; } - private File createEmptyVer4DictionaryAndGetFile(final int formatVersion) throws IOException { + private File createEmptyVer4DictionaryAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) throws IOException { final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); file.delete(); file.mkdir(); - Map<String, String> attributeMap = new HashMap<>(); if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, Locale.ENGLISH, attributeMap)) { return file; @@ -669,6 +677,12 @@ public class BinaryDictionaryTests extends AndroidTestCase { } private void testRandomOperationsAndFlashWithGC(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int flashWithGCIterationCount = 50; final int operationCountInEachIteration = 200; final int initialUnigramCount = 100; @@ -679,7 +693,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + attributeMap); BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final ArrayList<String> words = new ArrayList<>(); @@ -815,13 +830,20 @@ public class BinaryDictionaryTests extends AndroidTestCase { } private void testUnigramAndBigramCount(final int formatVersion) { + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); + final int flashWithGCIterationCount = 10; final int codePointSetSize = 50; final int unigramCountPerIteration = 1000; final int bigramCountPerIteration = 2000; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + final File dictFile = createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + attributeMap); final ArrayList<String> words = new ArrayList<>(); final HashSet<Pair<String, String>> bigrams = new HashSet<>(); diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java index 30b088137..ae5cc5c73 100644 --- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java +++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java @@ -28,7 +28,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_SPAN_START = 0; final int EXPECTED_SPAN_END = 4; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertEquals("show blue underline, span start", EXPECTED_SPAN_START, span.mStart); @@ -42,7 +42,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_SPAN_START = 0; final int EXPECTED_SPAN_END = 5; type(STRING_1_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(STRING_2_TO_TYPE); // We haven't have time to look into the dictionary yet, so the line should still be @@ -51,7 +51,7 @@ public class BlueUnderlineTests extends InputTestsBase { assertEquals("extend blue underline, span start", EXPECTED_SPAN_START, spanBefore.mStart); assertEquals("extend blue underline, span end", EXPECTED_SPAN_END, spanBefore.mEnd); assertTrue("extend blue underline, span color", spanBefore.isAutoCorrectionIndicator()); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); // Now we have been able to re-evaluate the word, there shouldn't be an auto-correction span final SpanGetter spanAfter = new SpanGetter(mEditText.getText(), SuggestionSpan.class); @@ -65,18 +65,18 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_UNDERLINE_SPAN_START = 0; final int EXPECTED_UNDERLINE_SPAN_END = 3; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_SPACE); // typedLength + 1 because we also typed a space mLatinIME.onUpdateSelection(0, 0, typedLength + 1, typedLength + 1, -1, -1); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter suggestionSpan = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertFalse("show no blue underline after backspace, span should not be the auto-" @@ -93,7 +93,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int typedLength = STRING_TO_TYPE.length(); final int NEW_CURSOR_POSITION = 0; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); // Simulate the onUpdateSelection() event mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); runMessages(); @@ -103,7 +103,7 @@ public class BlueUnderlineTests extends InputTestsBase { mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); mLatinIME.onUpdateSelection(typedLength, typedLength, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertFalse("blue underline removed when cursor is moved", @@ -113,7 +113,7 @@ public class BlueUnderlineTests extends InputTestsBase { public void testComposingStopsOnSpace() { final String STRING_TO_TYPE = "this "; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); // Simulate the onUpdateSelection() event mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); runMessages(); diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index ec249dab3..99dc9a204 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import android.test.MoreAsserts; import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.BaseInputConnection; @@ -487,7 +488,7 @@ public class InputLogicTests extends InputTestsBase { public void testPredictionsAfterSpace() { final String WORD_TO_TYPE = "Barack "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -499,17 +500,17 @@ public class InputLogicTests extends InputTestsBase { mLatinIME.clearPersonalizedDictionariesForTest(); final String WORD_TO_TYPE = "Barack "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // No need to test here, testPredictionsAfterSpace is testing it already type(" "); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the predictions have been cleared SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("predictions cleared after double-space-to-period", suggestedWords.size(), 0); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -522,7 +523,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the auto-correction. For "Barack", the auto-correction should be "Barack". pickSuggestionManually(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -534,13 +535,13 @@ public class InputLogicTests extends InputTestsBase { mLatinIME.clearPersonalizedDictionariesForTest(); final String WORD_TO_TYPE = "Barack. "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("No prediction after period after inputting once.", 0, suggestedWords.size()); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("Beginning-of-Sentence prediction after inputting 2 times.", "Barack", @@ -565,18 +566,18 @@ public class InputLogicTests extends InputTestsBase { type(" "); mLatinIME.onUpdateSelection(endOfSuggestion, endOfSuggestion, endOfSuggestion + 1, endOfSuggestion + 1, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Simulate a manual cursor move mInputConnection.setSelection(indexForManualCursor, indexForManualCursor); mLatinIME.onUpdateSelection(endOfSuggestion + 1, endOfSuggestion + 1, indexForManualCursor, indexForManualCursor, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); pickSuggestionManually(WORD_TO_TYPE); mLatinIME.onUpdateSelection(indexForManualCursor, indexForManualCursor, endOfWord, endOfWord, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -624,7 +625,7 @@ public class InputLogicTests extends InputTestsBase { for (int i = 0; i < WORD_TO_TYPE.length(); ++i) { type(WORD_TO_TYPE.substring(i, i+1)); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } assertEquals("type many trailing single quotes one by one", EXPECTED_RESULT, @@ -636,7 +637,7 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = WORD_TO_TYPE; for (int i = 0; i < WORD_TO_TYPE.length(); ++i) { type(WORD_TO_TYPE.substring(i, i+1)); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } assertEquals("type words letter by letter", EXPECTED_RESULT, @@ -652,10 +653,30 @@ public class InputLogicTests extends InputTestsBase { changeLanguage("fr"); runMessages(); type(WORD_TO_TYPE_SECOND_PART); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("Suggestions updated after switching languages", EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(1) : null); } + + public void testBasicGesture() { + gesture("this"); + assertEquals("gesture \"this\"", "this", mEditText.getText().toString()); + } + + public void testGestureGesture() { + gesture("this"); + gesture("is"); + assertEquals("gesture \"this is\"", "this is", mEditText.getText().toString()); + } + + public void testGestureBackspaceGestureAgain() { + gesture("this"); + type(Constants.CODE_DELETE); + assertEquals("gesture then backspace", "", mEditText.getText().toString()); + gesture("this"); + MoreAsserts.assertNotEqual("gesture twice the same thing", "this", + mEditText.getText().toString()); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java index 2560407dc..c16372ab5 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java @@ -74,7 +74,7 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { mInputConnection.setSelection(CURSOR_POS, CURSOR_POS); mLatinIME.onUpdateSelection(typedLength, typedLength, CURSOR_POS, CURSOR_POS, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); assertEquals("start composing inside text", -1, BaseInputConnection.getComposingSpanStart(mEditText.getText())); @@ -91,7 +91,7 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { final String WORD_TO_TYPE = "Barack "; changeKeyboardLocaleAndDictLocale("th", "en_US"); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Make sure there is no space assertEquals("predictions in lang without spaces", "Barack", diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java index 715d449a0..842b54fe1 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java @@ -70,7 +70,7 @@ public class InputLogicTestsNonEnglish extends InputTestsBase { try { changeLanguage("fr"); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); assertTrue("type word then type space should display punctuation strip", mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); @@ -95,7 +95,7 @@ public class InputLogicTestsNonEnglish extends InputTestsBase { try { changeLanguage("fr"); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("type word then type space yields predictions for French", diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 6860bea45..dd900a22c 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.SharedPreferences; +import android.graphics.Point; import android.os.Looper; import android.preference.PreferenceManager; import android.test.ServiceTestCase; @@ -39,10 +40,13 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.event.Event; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.Dictionary.PhonyDictionary; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 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.StringUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Locale; @@ -56,11 +60,17 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { private static final String DEFAULT_AUTO_CORRECTION_THRESHOLD = "1"; // The message that sets the underline is posted with a 500 ms delay - protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 500; + protected static final int DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS = 500; // The message that sets predictions is posted with a 200 ms delay - protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200; + protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS = 200; + // We wait for gesture computation for this delay + protected static final int DELAY_TO_WAIT_FOR_GESTURE_MILLIS = 200; private final int TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS = 60; + // Type for a test phony dictionary + private static final String TYPE_TEST = "test"; + private static final PhonyDictionary DICTIONARY_TEST = new PhonyDictionary(TYPE_TEST); + protected LatinIME mLatinIME; protected Keyboard mKeyboard; protected MyEditText mEditText; @@ -211,7 +221,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // Run messages to avoid the messages enqueued by startInputView() and its friends // to run on a later call and ruin things. We need to wait first because some of them // can be posted with a delay (notably, MSG_RESUME_SUGGESTIONS) - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } @@ -295,6 +305,47 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { } } + protected Point getXY(final int codePoint) { + final Key key = mKeyboard.getKey(codePoint); + if (key == null) { + throw new RuntimeException("Code point not on the keyboard"); + } else { + return new Point(key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2); + } + } + + protected void gesture(final String stringToGesture) { + if (StringUtils.codePointCount(stringToGesture) < 2) { + throw new RuntimeException("Can't gesture strings less than 2 chars long"); + } + + mLatinIME.onStartBatchInput(); + final int startCodePoint = stringToGesture.codePointAt(0); + Point oldPoint = getXY(startCodePoint); + int timestamp = 0; // In milliseconds since the start of the gesture + final InputPointers pointers = new InputPointers(Constants.DEFAULT_GESTURE_POINTS_CAPACITY); + pointers.addPointer(oldPoint.x, oldPoint.y, 0 /* pointerId */, timestamp); + + for (int i = Character.charCount(startCodePoint); i < stringToGesture.length(); + i = stringToGesture.offsetByCodePoints(i, 1)) { + final Point newPoint = getXY(stringToGesture.codePointAt(i)); + // Arbitrarily 0.5s between letters and 0.1 between events. Refine this later if needed. + final int STEPS = 5; + for (int j = 0; j < STEPS; ++j) { + timestamp += 100; + pointers.addPointer(oldPoint.x + ((newPoint.x - oldPoint.x) * j) / STEPS, + oldPoint.y + ((newPoint.y - oldPoint.y) * j) / STEPS, + 0 /* pointerId */, timestamp); + } + oldPoint.x = newPoint.x; + oldPoint.y = newPoint.y; + mLatinIME.onUpdateBatchInput(pointers); + } + mLatinIME.onEndBatchInput(pointers); + sleep(DELAY_TO_WAIT_FOR_GESTURE_MILLIS); + runMessages(); + } + protected void waitForDictionariesToBeLoaded() { try { mLatinIME.waitForLoadingDictionaries( @@ -353,7 +404,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { protected void pickSuggestionManually(final String suggestion) { mLatinIME.pickSuggestionManually(new SuggestedWordInfo(suggestion, 1, - SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */, + SuggestedWordInfo.KIND_CORRECTION, DICTIONARY_TEST, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java index 64750fbda..3537918de 100644 --- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java +++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java @@ -38,7 +38,7 @@ public class PunctuationTests extends InputTestsBase { try { mLatinIME.loadSettings(); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); assertTrue("type word then type space should display punctuation strip", mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); diff --git a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java index db3c9baa9..8ba0174b5 100644 --- a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java +++ b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java @@ -75,7 +75,7 @@ public class ShiftModeTests extends InputTestsBase { repeatKey(Constants.CODE_DELETE); } assertFalse("Caps immediately after repeating Backspace a lot", isCapsModeAutoShifted()); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); assertTrue("Caps after a while after repeating Backspace a lot", isCapsModeAutoShifted()); } diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 563261f8f..221541e4a 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -59,40 +59,6 @@ public class SuggestedWordsTests extends AndroidTestCase { SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); } - public void testGetSuggestedWordsExcludingTypedWord() { - final String TYPED_WORD = "typed"; - final int NUMBER_OF_ADDED_SUGGESTIONS = 5; - final int KIND_OF_SECOND_CORRECTION = SuggestedWordInfo.KIND_CORRECTION; - final ArrayList<SuggestedWordInfo> list = new ArrayList<>(); - list.add(createTypedWordInfo(TYPED_WORD)); - for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) { - list.add(createCorrectionWordInfo(Integer.toString(i))); - } - - final SuggestedWords words = new SuggestedWords( - list, null /* rawSuggestions */, - false /* typedWordValid */, - false /* willAutoCorrect */, - false /* isObsoleteSuggestions */, - SuggestedWords.INPUT_STYLE_NONE); - assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size()); - assertEquals("typed", words.getWord(0)); - assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED)); - assertEquals("0", words.getWord(1)); - assertTrue(words.getInfo(1).isKindOf(KIND_OF_SECOND_CORRECTION)); - assertEquals("4", words.getWord(5)); - assertTrue(words.getInfo(5).isKindOf(KIND_OF_SECOND_CORRECTION)); - - final SuggestedWords wordsWithoutTyped = - words.getSuggestedWordsExcludingTypedWordForRecorrection(); - // Make sure that the typed word has indeed been excluded, by testing the size of the - // suggested words, the string and the kind of the top suggestion, which should match - // the string and kind of what we inserted after the typed word. - assertEquals(words.size() - 1, wordsWithoutTyped.size()); - assertEquals("0", wordsWithoutTyped.getWord(0)); - assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(KIND_OF_SECOND_CORRECTION)); - } - // Helper for testGetTransformedWordInfo private SuggestedWordInfo transformWordInfo(final String info, final int trailingSingleQuotesCount) { @@ -141,9 +107,14 @@ public class SuggestedWordsTests extends AndroidTestCase { assertNotNull(typedWord); assertEquals(TYPED_WORD, typedWord.mWord); - // Make sure getTypedWordInfoOrNull() returns null. - final SuggestedWords wordsWithoutTypedWord = - wordsWithTypedWord.getSuggestedWordsExcludingTypedWordForRecorrection(); + // Make sure getTypedWordInfoOrNull() returns null when no typed word. + list.remove(0); + final SuggestedWords wordsWithoutTypedWord = new SuggestedWords( + list, null /* rawSuggestions */, + false /* typedWordValid */, + false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_NONE); assertNull(wordsWithoutTypedWord.getTypedWordInfoOrNull()); // Make sure getTypedWordInfoOrNull() returns null. diff --git a/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java index 00857e54e..832817967 100644 --- a/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java +++ b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java @@ -23,7 +23,7 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.test.AndroidTestCase; -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.settings.LocalSettingsConstants; /** * Tests for {@link AccountsChangedReceiver}. @@ -40,7 +40,7 @@ public class AccountsChangedReceiverTests extends AndroidTestCase { super.setUp(); mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); // Keep track of the current account so that we restore it when the test finishes. - mLastKnownAccount = mPrefs.getString(Settings.PREF_ACCOUNT_NAME, null); + mLastKnownAccount = mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null); } @Override @@ -99,13 +99,14 @@ public class AccountsChangedReceiverTests extends AndroidTestCase { private void updateAccountName(String accountName) { if (accountName == null) { - mPrefs.edit().remove(Settings.PREF_ACCOUNT_NAME).apply(); + mPrefs.edit().remove(LocalSettingsConstants.PREF_ACCOUNT_NAME).apply(); } else { - mPrefs.edit().putString(Settings.PREF_ACCOUNT_NAME, accountName).apply(); + mPrefs.edit().putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, accountName).apply(); } } private void assertAccountName(String expectedAccountName) { - assertEquals(expectedAccountName, mPrefs.getString(Settings.PREF_ACCOUNT_NAME, null)); + assertEquals(expectedAccountName, + mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null)); } } diff --git a/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java index d151732aa..fed8be920 100644 --- a/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java +++ b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java @@ -16,8 +16,8 @@ package com.android.inputmethod.latin.network; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -53,41 +53,52 @@ public class BlockingHttpClientTests extends AndroidTestCase { MockitoAnnotations.initMocks(this); } - public void testError_badGateway() throws IOException { + public void testError_badGateway() throws IOException, AuthException { when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_BAD_GATEWAY); final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); - final FakeErrorResponseProcessor processor = - new FakeErrorResponseProcessor(HttpURLConnection.HTTP_BAD_GATEWAY); - - client.execute(null /* empty request */, processor); - assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + final FakeErrorResponseProcessor processor = new FakeErrorResponseProcessor(); + + try { + client.execute(null /* empty request */, processor); + fail("Expecting an HttpException"); + } catch (HttpException e) { + // expected HttpException + assertEquals(HttpURLConnection.HTTP_BAD_GATEWAY, e.getHttpStatusCode()); + } } - public void testError_clientTimeout() throws IOException { + public void testError_clientTimeout() throws Exception { when(mMockHttpConnection.getResponseCode()).thenReturn( HttpURLConnection.HTTP_CLIENT_TIMEOUT); final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); - final FakeErrorResponseProcessor processor = - new FakeErrorResponseProcessor(HttpURLConnection.HTTP_CLIENT_TIMEOUT); - - client.execute(null /* empty request */, processor); - assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + final FakeErrorResponseProcessor processor = new FakeErrorResponseProcessor(); + + try { + client.execute(null /* empty request */, processor); + fail("Expecting an HttpException"); + } catch (HttpException e) { + // expected HttpException + assertEquals(HttpURLConnection.HTTP_CLIENT_TIMEOUT, e.getHttpStatusCode()); + } } - public void testError_forbiddenWithRequest() throws IOException { + public void testError_forbiddenWithRequest() throws Exception { final OutputStream mockOutputStream = Mockito.mock(OutputStream.class); when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN); when(mMockHttpConnection.getOutputStream()).thenReturn(mockOutputStream); final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); - final FakeErrorResponseProcessor processor = - new FakeErrorResponseProcessor(HttpURLConnection.HTTP_FORBIDDEN); + final FakeErrorResponseProcessor processor = new FakeErrorResponseProcessor(); - client.execute(new byte[100], processor); + try { + client.execute(new byte[100], processor); + fail("Expecting an HttpException"); + } catch (HttpException e) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, e.getHttpStatusCode()); + } verify(mockOutputStream).write(any(byte[].class), eq(0), eq(100)); - assertTrue("ResponseProcessor was not invoked", processor.mInvoked); } - public void testSuccess_emptyRequest() throws IOException { + public void testSuccess_emptyRequest() throws Exception { final Random rand = new Random(); byte[] response = new byte[100]; rand.nextBytes(response); @@ -101,7 +112,7 @@ public class BlockingHttpClientTests extends AndroidTestCase { assertTrue("ResponseProcessor was not invoked", processor.mInvoked); } - public void testSuccess() throws IOException { + public void testSuccess() throws Exception { final OutputStream mockOutputStream = Mockito.mock(OutputStream.class); final Random rand = new Random(); byte[] response = new byte[100]; @@ -117,28 +128,15 @@ public class BlockingHttpClientTests extends AndroidTestCase { assertTrue("ResponseProcessor was not invoked", processor.mInvoked); } - private static class FakeErrorResponseProcessor implements ResponseProcessor { - private final int mExpectedStatusCode; - - boolean mInvoked; - - FakeErrorResponseProcessor(int expectedStatusCode) { - mExpectedStatusCode = expectedStatusCode; - } - + private static class FakeErrorResponseProcessor implements ResponseProcessor<Void> { @Override - public void onError(int httpStatusCode, String message) { - mInvoked = true; - assertEquals("onError:", mExpectedStatusCode, httpStatusCode); - } - - @Override - public void onSuccess(InputStream response) { + public Void onSuccess(InputStream response) { fail("Expected an error but received success"); + return null; } } - private static class FakeSuccessResponseProcessor implements ResponseProcessor { + private static class FakeSuccessResponseProcessor implements ResponseProcessor<Void> { private final byte[] mExpectedResponse; boolean mInvoked; @@ -148,12 +146,7 @@ public class BlockingHttpClientTests extends AndroidTestCase { } @Override - public void onError(int httpStatusCode, String message) { - fail("Expected a response but received an error"); - } - - @Override - public void onSuccess(InputStream response) { + public Void onSuccess(InputStream response) { try { mInvoked = true; BufferedInputStream in = new BufferedInputStream(response); @@ -169,6 +162,7 @@ public class BlockingHttpClientTests extends AndroidTestCase { } catch (IOException ex) { fail("IOException in onSuccess"); } + return null; } } } diff --git a/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java b/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java index 2b43d5b14..5b3e78eaf 100644 --- a/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java +++ b/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java @@ -142,4 +142,13 @@ public class HttpUrlConnectionBuilderTests extends AndroidTestCase { assertTrue(connection.getDoInput()); assertTrue(connection.getDoOutput()); } + + public void testSetAuthToken() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("https://www.example.com"); + builder.setAuthToken("some-random-auth-token"); + HttpURLConnection connection = builder.build(); + assertEquals("some-random-auth-token", + connection.getRequestProperty(HttpUrlConnectionBuilder.HTTP_HEADER_AUTHORIZATION)); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java b/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java deleted file mode 100644 index 58312264b..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2010 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.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -@SmallTest -public class EditDistanceTests extends AndroidTestCase { - /* - * dist(kitten, sitting) == 3 - * - * kitten- - * .|||.| - * sitting - */ - public void testExample1() { - final int dist = BinaryDictionaryUtils.editDistance("kitten", "sitting"); - assertEquals("edit distance between 'kitten' and 'sitting' is 3", - 3, dist); - } - - /* - * dist(Sunday, Saturday) == 3 - * - * Saturday - * | |.||| - * S--unday - */ - public void testExample2() { - final int dist = BinaryDictionaryUtils.editDistance("Saturday", "Sunday"); - assertEquals("edit distance between 'Saturday' and 'Sunday' is 3", - 3, dist); - } - - public void testBothEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("", ""); - assertEquals("when both string are empty, no edits are needed", - 0, dist); - } - - public void testFirstArgIsEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("", "aaaa"); - assertEquals("when only one string of the arguments is empty," - + " the edit distance is the length of the other.", - 4, dist); - } - - public void testSecoondArgIsEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("aaaa", ""); - assertEquals("when only one string of the arguments is empty," - + " the edit distance is the length of the other.", - 4, dist); - } - - public void testSameStrings() { - final String arg1 = "The quick brown fox jumps over the lazy dog."; - final String arg2 = "The quick brown fox jumps over the lazy dog."; - final int dist = BinaryDictionaryUtils.editDistance(arg1, arg2); - assertEquals("when same strings are passed, distance equals 0.", - 0, dist); - } - - public void testSameReference() { - final String arg = "The quick brown fox jumps over the lazy dog."; - final int dist = BinaryDictionaryUtils.editDistance(arg, arg); - assertEquals("when same string references are passed, the distance equals 0.", - 0, dist); - } - - public void testNullArg() { - try { - BinaryDictionaryUtils.editDistance(null, "aaa"); - fail("IllegalArgumentException should be thrown."); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - BinaryDictionaryUtils.editDistance("aaa", null); - fail("IllegalArgumentException should be thrown."); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} |