Merge "[Rlog81a] Determine correction type of words"
diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml
index cc04ced..2a90ba2 100644
--- a/java/res/layout/research_feedback_fragment_layout.xml
+++ b/java/res/layout/research_feedback_fragment_layout.xml
@@ -70,6 +70,14 @@
android:text="@string/research_feedback_include_history_label"
/>
+ <CheckBox
+ android:id="@+id/research_feedback_include_account_name"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginBottom="8dip"
+ android:checked="false"
+ android:text="@string/research_feedback_include_account_name_label"
+ />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 1ebe4d3..92de687 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Sleutelopspringer-wagperiode"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen wagperiode nie"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Verstek"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms."</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms."</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Stel kontakname voor"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gebruik name van kontakte vir voorstelle en korreksies"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dubbelspasie-punt"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Bruikbaarheidstudie-modus"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Sleuteldruk se vibrasie-tydsduurinstellings"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Sleuteldruk se klankvolume-instellings"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Lees eksterne woordeboeklêer"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Verstek"</string>
</resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 21dab07..c5c02da 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"የቁልፍ ብቅ ባይ መዘግየትን ያስወገዳል"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"የዘገየ የለም"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ነባሪ"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ሚሊሰከንድ"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ሚሊሰከንድ"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"የዕውቂያ ስም ጠቁም"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"ከዕውቂያዎች ለጥቆማዎች እና ማስተካከያዎች ስሞች ተጠቀም"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"የድርብ-ክፍተት ነጥብ"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"የተገልጋይነት ጥናት ሁነታ"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"ቁልፍ ተጫን በቅንጅቶች ወቅት ንዝረት"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"ቁልፍ ተጫን የድምጽ መጠን ቅንብሮች"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"ውጫዊ የመዝገበቃላት ፋይል አንብብ"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"ነባሪ"</string>
</resources>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 64c8b65..3282727 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخير إزالة النافذة المنبثقة الأساسية"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بلا تأخير"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"افتراضي"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> مللي ثانية"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> مللي ثانية"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"اقتراح أسماء جهات الاتصال"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"استخدام الأسماء من جهات الاتصال للاقتراحات والتصحيحات"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"نقطة المسافة المزدوجة"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"وضع سهولة الاستخدام"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"إعدادات مدة اهتزاز الضغط على المفاتيح"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"إعدادات مستوى صوت الضغط على المفاتيح"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"قراءة ملف قاموس خارجي"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"الافتراضية"</string>
</resources>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index f5d81ea..ad9fb77 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затрым. скр. падк. клав."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Няма затрымкі"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Прапан. імёны кантактаў"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Выкарыстоўваць імёны са спісу кантактаў для прапаноў і выпраўл."</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Падвойны iнтэрвал"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Рэжым даследвання выкарыстальнасці"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Налады працягласцi вiбрацыi пры нацiску"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Налады гучнасцi пры нацiску"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Чытанне знешняга файла слоўніка"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Па змаўчанні"</string>
</resources>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index abffe8f..588e076 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Отхвърляне на подсказката"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задържане"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По подразбиране"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> милисек"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> милисек"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Предложения за контакти"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Използване на имена от „Контакти“ за предложения и поправки"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Точка чрез двоен интервал"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за изучаване на използваемостта"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Настройки за продължителност на вибрирането при натискане на клавиш"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Настройки за силата на звука при натискане на клавиш"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Четене на файл за външен речник"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Стандартни"</string>
</resources>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index e369bda..796374c 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard en ampliar tecla"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sense retard"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms de contactes"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de contactes per fer suggeriments i correccions"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Punt amb doble espai"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'estudi d\'usabilitat"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Configuració de durada de vibracions en prémer tecles"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Configuració del volum de so en prémer tecles"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Lectura d\'un fitxer de diccionari extern"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Predeterminat"</string>
</resources>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 065ddf1..3316819 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Prodleva vysk. okna klávesnice"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez prodlevy"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Navrhovat jména kontaktů"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Použít jména ze seznamu kontaktů k návrhům a opravám"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Tečka dvojitým mezerníkem"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim studie použitelnosti"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Délka vibrace při stisku klávesy"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Hlasitost při stisknutí klávesy"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Číst soubor externího slovníku"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Výchozí"</string>
</resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 5adaf40..8a93ba9 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Forsink. afvis. af taste-pop op"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ingen forsink."</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå navne på kontakter"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Brug navne fra Kontaktpersoner til forslag og rettelser"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"To mellemrum for punktum"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tilstand for brugsstudie"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Indstillinger for varighed af vibration ved tastetryk"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Indstillinger for lydstyrke ved tastetryk"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Læs ekstern ordbogsfil"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Standard"</string>
</resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 3739dbf..1a0a017 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tasten-Pop-up"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Keine Verzögerung"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Kontakte vorschlagen"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Punkt plus Leerzeichen"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Studie zur Benutzerfreundlichkeit"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Vibrationsdauer bei Tastendruck"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Tonlautstärke bei Tastendruck"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Externe Wörterbuchdatei lesen"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Standard"</string>
</resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 0c08596..3ac4e47 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Χρόνος εξαφ. αναδ. παραθ."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Χωρίς καθυστέρ."</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Προεπιλογή"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Πρόταση ονομάτων επαφών"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Χρησιμοποιήστε ονόματα από τις Επαφές για προτάσεις και διορθ."</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Τελεία με διπλό πάτημα πλήκτρ.διαστ."</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Λειτουργία μελέτης χρηστικότητας"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Ρυθμίσεις διάρκειας δόνησης κατά το πάτημα πλήκτρων"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Ρυθμίσεις έντασης ήχου κατά το πάτημα πλήκτρων"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Ανάγνωση εξωτερικού αρχείου λεξικού"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Προεπιλογή"</string>
</resources>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 4795df2..df3b238 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Key pop-up dismiss delay"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"No delay"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Double-space full stop"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Usability study mode"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Key-press vibration duration settings"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Key-press sound volume settings"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Read external dictionary file"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Default"</string>
</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index a3ce391..7e67f29 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso en rechazo de alerta de tecla"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nombres de contacto"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nombres de los contactos para sugerencias y correcciones"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Punto y doble espacio"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudio de usabilidad"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Duración de vibración al presionar teclas"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Volumen de sonido al presionar teclas"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Leer archivo de diccionario externo"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string>
</resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 452ed75..6c39ceb 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso al ampliar tecla"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin retraso"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir contactos"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nombres de contactos para sugerencias y correcciones"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Punto y espacio"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo estudio de usabilidad"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Duración de la vibración al pulsar tecla"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Volumen sonido al pulsar tecla"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Leer archivo de diccionario externo"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Predeterminado"</string>
</resources>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index 48ce7f6..1d2b029 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Hüpiku loobumisviivitus"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Viivituseta"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Soovita kontaktkirjeid"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Kasuta soovitusteks ja parandusteks nimesid kontaktiloendist"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Punkt tühikuklahviga"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Kasutatavuse uurimisrežiim"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Klahvivajutuse vibratsiooni kestuse seaded"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Klahvivajutuse helitugevuse seaded"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Välise sõnastikufaili lugemine"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Vaikeväärtus"</string>
</resources>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index a40fb34..f9a3e2e 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخیر در رد کردن کلید نمایشی"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بدون تأخیر"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیشفرض"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>میلی ثانیه"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> میلیثانیه"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نامهای مخاطب"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"برای پیشنهاد و تصحیح از نام مخاطبین استفاده شود"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"نقطه با دو فاصله"</string>
@@ -150,4 +150,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"حالت بررسی قابلیت استفاده"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"تنظیمات مدت زمان لرزش فشار کلید"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"تنظیمات میزان صدای فشار کلید"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"خواندن فایل فرهنگ لغت خارجی"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"پیشفرض"</string>
</resources>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 1b87ae8..24f06e2 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Näppäimen hylkäysviive"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ei viivettä"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Ehdota yhteystietojen nimiä"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Käytä yhteystietojen nimiä ehdotuksissa ja korjauksissa"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Kaksoisvälilyönti = piste"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Käytettävyystutkimustila"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Näppäimenpainalluksen värinän kestoasetukset"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Näppäimenpainalluksen äänenvoimakkuusasetukset"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Lue ulkoista sanakirjatiedostoa"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Oletusarvot"</string>
</resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 6471db0..8e86888 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Masquer touche agrandie"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sans délai"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Point et espace"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'étude de l\'utilisabilité"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Durée de vibration à chaque pression"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Volume sonore à chaque pression"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Lire un fichier de dictionnaire externe"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Par défaut"</string>
</resources>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 1333f0f..44ea37f 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"कुंजी पॉपअप खारिज़ विलंब"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"कोई विलंब नहीं"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"डिफ़ॉल्ट"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> मिलीसेकंड"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> मिलीसेकंड"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"संपर्क नाम सुझाएं"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव और सुधार के लिए संपर्क से नामों का उपयोग करें"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"दोहरे स्पेस वाला पीरियड"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"उपयोगिता अध्ययन मोड"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"कुंजी-स्पर्श कंपन अवधि सेटिंग"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"कुंजी-स्पर्श ध्वनि वॉल्यूम सेटिंग"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"बाहरी डिक्शनरी फ़ाइल पढ़ें"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"डिफ़ॉल्ट"</string>
</resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index fd627c3..f8db8c9 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Odgoda prikaza tipki"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez odgode"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Predlaži imena kontakata"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Upotreba imena iz Kontakata za prijedloge i ispravke"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Točka s dva razmaka"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način studije upotrebljivosti"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Postavke trajanja vibracije kod pritiska tipke"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Postavke glasnoće zvuka kod pritiska tipke"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Čitanje datoteke vanjskog rječnika"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Zadano"</string>
</resources>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index c58bb60..2d93847 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Gombeltüntetés késése"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nincs késés"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Javasolt névjegyek"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"A névjegyek használata a javaslatokhoz és javításokhoz"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dupla szóköz: pont"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Használhatósági teszt"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Gombnyomás rezgési időtartamának beállításai"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Gombnyomás hangerejének beállításai"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Külső szótárfájl olvasása"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Alapértelmezett"</string>
</resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index c24c910..b2d4665 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tundaan singkir munculan kunci"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tanpa penundaan"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> md"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> md"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sarankan nama Kontak"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama dari Kontak untuk saran dan koreksi"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Titik spasi ganda"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode studi daya guna"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Setelan durasi getaran saat tombol ditekan"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Setelan volume suara saat tombol ditekan"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Membaca file kamus eksternal"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Default"</string>
</resources>
diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml
index 016a1d1..c5d45b9 100644
--- a/java/res/values-is/strings.xml
+++ b/java/res/values-is/strings.xml
@@ -64,7 +64,7 @@
<skip />
<!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
<skip />
- <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
<skip />
<!-- no translation found for use_contacts_dict (4435317977804180815) -->
<skip />
@@ -267,4 +267,16 @@
<skip />
<!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
<skip />
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <!-- no translation found for button_default (3988017840431881491) -->
+ <skip />
</resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 98dc467..704cc14 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ritardo eliminaz. popup tasto"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nessun ritardo"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggerisci nomi di contatti"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizza nomi di Contatti per suggerimenti e correzioni"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Doppio spazio per punto"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modalità Studio sull\'usabilità"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Durata vibrazione alla pressione tasto"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Volume audio alla pressione di un tasto"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Leggi file dizionario esterno"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Predefinito"</string>
</resources>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 6f642c7..6775986 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"עיכוב סגירת חלון קופץ של מקש"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ללא עיכוב"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ברירת מחדל"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> אלפ\' שניה"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> אלפ\' שניה"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"הצע שמות של אנשי קשר"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"השתמש בשמות מרשימת אנשי הקשר עבור הצעות ותיקונים"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"רווח כפול לנקודה"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"מצב מחקר שימושיות"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"הגדרות משך רטט בלחיצה על מקש"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"הגדרות עוצמת קול בלחיצה על מקש"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"קריאה של קובץ מילון חיצוני"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"ברירת מחדל"</string>
</resources>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index df9c054..17e32e4 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"キーのポップアップ時間"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"すぐに消去"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"デフォルト"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ミリ秒"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ミリ秒"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"候補の連絡先名を表示"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"連絡先の名前を使用して候補表示や自動修正を行います"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"ダブルスペースピリオド"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"使いやすさの研究モード"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"キー操作バイブの振動時間の設定"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"キー操作音の音量設定"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"外部辞書ファイルの読み取り"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"デフォルト"</string>
</resources>
diff --git a/java/res/values-ka/strings.xml b/java/res/values-ka/strings.xml
index b9ffbf6..10d587a 100644
--- a/java/res/values-ka/strings.xml
+++ b/java/res/values-ka/strings.xml
@@ -64,7 +64,7 @@
<skip />
<!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
<skip />
- <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
<skip />
<!-- no translation found for use_contacts_dict (4435317977804180815) -->
<skip />
@@ -267,4 +267,16 @@
<skip />
<!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
<skip />
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <!-- no translation found for button_default (3988017840431881491) -->
+ <skip />
</resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 1f2a833..c58d73c 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"키 팝업 해제 지연"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"지연 없음"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"주소록 이름 활용"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"추천 및 수정에 주소록의 이름 사용"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"더블스페이스 마침표"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"가용성 연구 모드"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"키를 누를 때 진동 시간 설정"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"키를 누를 때 효과음 설정"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"외부 사전 파일 읽기"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"기본값"</string>
</resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index bb295c9..3e58791 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pagr. išš. l. atsis. d."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Be delsos"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Siūlyti kontaktų vardus"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Siūlant ir taisant naudoti vardus iš „Kontaktų“"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Tšk. ir tarp. pal. dukart"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tinkamumo tyrimo režimas"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Vibracijos paspaudus mygtuką trukmės nustatymai"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Garso paspaudus mygtuką garsumo nustatymai"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Skaityti išorinį žodyno failą"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Numatytieji"</string>
</resources>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 4b55226..95bed36 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Taust. uzn. loga noraid. aizk."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez aizkaves"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Ieteikt kontaktp. vārdus"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Izmantot kontaktpersonu vārdus kā ieteikumus un labojumus"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dubultpiesk. = punkts"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Lietojamības izpētes režīms"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Taustiņu nospiešanas vibrācijas ilguma iestatījumi"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Taustiņu nospiešanas skaņas skaļuma iestatījumi"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Ārējās vārdnīcas faila nolasīšana"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Noklusējums"</string>
</resources>
diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml
index 7758dbb..3b21023 100644
--- a/java/res/values-mk/strings.xml
+++ b/java/res/values-mk/strings.xml
@@ -64,7 +64,7 @@
<skip />
<!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
<skip />
- <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
<skip />
<!-- no translation found for use_contacts_dict (4435317977804180815) -->
<skip />
@@ -267,4 +267,16 @@
<skip />
<!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
<skip />
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <!-- no translation found for button_default (3988017840431881491) -->
+ <skip />
</resources>
diff --git a/java/res/values-mn/strings.xml b/java/res/values-mn/strings.xml
index 4d38079..3b8f21b 100644
--- a/java/res/values-mn/strings.xml
+++ b/java/res/values-mn/strings.xml
@@ -64,7 +64,7 @@
<skip />
<!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
<skip />
- <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
<skip />
<!-- no translation found for use_contacts_dict (4435317977804180815) -->
<skip />
@@ -267,4 +267,16 @@
<skip />
<!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
<skip />
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <!-- no translation found for button_default (3988017840431881491) -->
+ <skip />
</resources>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index 0e03b9a..fa2297e 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -42,7 +42,8 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pop tmbl knci ketpkn lengah"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tiada lengah"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
+ <skip />
<string name="use_contacts_dict" msgid="4435317977804180815">"Cadangkan nama Kenalan"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama daripada Kenalan untuk cadangan dan pembetulan"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Titik ruang berganda"</string>
@@ -146,4 +147,15 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mod kajian kebolehgunaan"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Tetapan tempoh getaran tekan kekunci"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Tetapan kelantangan bunyi tekanan kekunci"</string>
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Lalai"</string>
</resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 4232e4a..646f1af 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tregt tastevindu"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"U/ forsinkelse"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå kontaktnavn"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Bruk navn fra Kontakter til forslag og korrigeringer"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Punktum ved doble mellomrom"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Bruksstudiemodus"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Innstillinger for vibrasjonsvarighet ved tastetrykk"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Innstillinger for lydstyrke ved tastetrykk"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Les en ekstern ordlistefil"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Standard"</string>
</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index b2d60a7..5b236dc 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Afwijz.vertr. toetspop-up"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen vertraging"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Contactnamen suggereren"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen uit Contacten gebruiken voor suggesties en correcties"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dubbeltik is punt, spatie"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus voor gebruiksvriendelijkheidsonderzoek"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Instellingen voor trillingsduur bij druk op een toets"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Instellingen voor geluidsvolume bij druk op een toets"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Extern woordenboekbestand lezen"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Standaard"</string>
</resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 370eeed..893de38 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Opóźnienie znikania klawiszy"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez opóźnienia"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Proponuj osoby z kontaktów"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"W propozycjach i poprawkach użyj nazwisk z kontaktów"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Szybka kropka ze spacją"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tryb badania przydatności"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Czas trwania wibracji przy naciśnięciu"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Głośność dźwięku przy naciśnięciu"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Odczyt zewnętrznego pliku słownika"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Domyślne"</string>
</resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index f155597..84c0ce5 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Atraso p/ ignorar pop-up"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de Contactos"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nomes dos Contactos para sugestões e correções"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Ponto de espaço duplo"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo da capacidade de utilização"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Definições de duração da vibração ao premir as teclas"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Definições de volume de som ao premir as teclas"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Ler ficheiro de dicionário externo"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Predefinido"</string>
</resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 12e9648..80d9171 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -42,7 +42,8 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Dispens. atraso chave princ."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
+ <skip />
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de contato"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nomes dos Contatos para sugestões e correções"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Duplo espaço p/ ponto"</string>
@@ -146,4 +147,15 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo de utilização"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Configurações de duração da vibração ao tocar a tecla"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Config. volume ao tocar a tecla"</string>
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Padrão"</string>
</resources>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 90d7357..879174b 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -61,7 +61,7 @@
<skip />
<!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
<skip />
- <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+ <!-- no translation found for abbreviation_unit_milliseconds (8700286094028323363) -->
<skip />
<!-- no translation found for use_contacts_dict (4435317977804180815) -->
<skip />
@@ -259,4 +259,16 @@
<skip />
<!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
<skip />
+ <!-- no translation found for prefs_read_external_dictionary (2588931418575013067) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <!-- no translation found for button_default (3988017840431881491) -->
+ <skip />
</resources>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 50adadd..1484d4a 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Înt. înch. pop-up esenţ."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fără întârziere"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> msec."</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> msec."</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugeraţi nume din Agendă"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizaţi numele din Agendă pentru sugestii şi corecţii"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Inserează punct spațiu"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modul Studiu privind utilizarea"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Setări pentru durata vibrării la apăsarea tastei"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Setări pentru volumul sunetului la apăsarea tastei"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Citiți fișierul de dicționar extern"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Prestabilit"</string>
</resources>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index e92a2f8..c15695b 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Задержка закрытия"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задержки"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Подсказывать имена"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Подсказывать исправления на основе имен из списка контактов"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Точка с пробелом"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим проверки удобства использования"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Настройки вибросигнала при нажатии клавиш"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Настройки громкости звука при нажатии клавиш"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Считывать данные из внешнего словаря"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"По умолчанию"</string>
</resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index fd84a7f..101f47b 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Onesk. zrušenia kľúč. kon. okna"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez oneskorenia"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Navrhnúť mená kontaktov"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Používať mená z Kontaktov na návrhy a opravy"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Bodka s medzerou"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim štúdie použiteľnosti"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Nastavenia trvania vibrovania pri stlačení klávesu"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Nastavenia hlasitosti zvuku pri stlačení klávesu"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Čítať súbor externého slovníka"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Predvolené"</string>
</resources>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 9da5401..698443c 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Trajanje povečanja tipke"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Brez zakasnitve"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Predlagaj imena stikov"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Uporaba imen iz stikov za predloge in popravke"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dva presl. za vnos pike"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način za preučevanje uporabnosti"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Nastavitve za trajanje vibriranja ob pritisku tipke"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Nastavitve za glasnost zvoka ob pritisku tipke"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Branje zunanje datoteke slovarja"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Privzeto"</string>
</resources>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index ad0029e..58708b0 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Одложи одбац. иск. прозора тастера"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без одлагања"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Предложи имена контаката"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Користи имена из Контаката за предлоге и исправке"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Тачка и размак"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за студију могућности коришћења"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Подешавања трајања вибрације при притиску на тастере"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Подешавања јачине звука при притиску на тастере"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Читање датотеке спољног речника"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Подразумевано"</string>
</resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 403efcf..ef544a6 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ta bort popup-fördröjning"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fördröj inte"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> millisek."</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> millisek."</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Föreslå kontaktnamn"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Använd namn från Kontakter för förslag och korrigeringar"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dubbelt blanksteg = punkt"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Läge för studie av användbarhet"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Inställningar för vibrationslängd vid knapptryck"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Volyminställningar för knappljud"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Läs extern ordboksfil"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Standard"</string>
</resources>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 7202181..4bbe9c0 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Kuchelewesha kutupa kitufe ibukizi"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Hakuna kuchelewa"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Chaguo-msingi"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"Millisekunde <xliff:g id="MILLISECONDS">%s</xliff:g>"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"Millisekunde <xliff:g id="MILLISECONDS">%s</xliff:g>"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Pendekeza majini ya Anwani"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Tumia majina kutoka kwa Anwani kwa mapendekezo na marekebisho"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Kitone baada ya nafasi mbili"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modi ya uchunguzi wa utumizi"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Bonyeza mipangilio ya kipindi cha mtetemo"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Bonyeza mipangilio ya nguvu za sauti"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Soma faili ya nje ya kamusi"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Chaguo-msingi"</string>
</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index e42c53b..a70500e 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"หน่วงเวลาก่อนปิดป๊อปอัพหลัก"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ไม่มีการหน่วงเวลา"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> มิลลิวิ"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> มิลลิวิ"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"แนะนำชื่อผู้ติดต่อ"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"ใช้ชื่อจากรายชื่อติดต่อสำหรับคำแนะนำและการแก้ไข"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"แตะ Space สองครั้งแทรกจุด"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"โหมดศึกษาประโยชน์ในการใช้งาน"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"การตั้งค่าระยะเวลาการสั่นเมื่อกดแป้นพิมพ์"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"การตั้งค่าระดับเสียงเมื่อกดแป้นพิมพ์"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"อ่านไฟล์พจนานุกรมภายนอก"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"ค่าเริ่มต้น"</string>
</resources>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 530dc5d..3c93dd5 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Balewala antala key popup"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Walang antala"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Mungkahi pangalan Contact"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gamitin pangalan mula Mga Contact sa mga mungkahi\'t pagwawasto"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Double-space period"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Study mode ng pagiging kapaki-pakinabang"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Mga setting ng tagal ng vibration ng keypress"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Mga setting ng volume ng tunog ng keypress"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Magbasa ng panlabas na file ng diksyunaryo"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Default"</string>
</resources>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index e98a31e..f2c5f24 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tuş popup\'ının kapatılmasını geciktirme"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikme yok"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Kişi Adları öner"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Öneri ve düzeltmeler için Kişiler\'deki adları kullan"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Çift boşlukla nokta ekleme"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Kullanılabilirlik çalışması modu"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Tuşa basma titreşim süresi ayarları"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Tuşa basma ses düzeyi ayarları"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Harici sözlük dosyasını oku"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Varsayılan"</string>
</resources>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index f6688c2..f2de1be 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затримка клавіши закриття"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без затримки"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Пропон. імена контактів"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Використ. імена зі списку контактів для пропозицій і виправлень"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Крапка подвійним пробілом"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим вивчення зручності у використанні"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Налаштування тривалості вібрації під час натискання клавіші"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Налаштування гучності звуку під час натискання клавіші"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Читати файл зовнішнього словника"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"За умовчанням"</string>
</resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index efb1291..7ba032f 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Loại bỏ hiển thị phím trễ"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Không có tgian trễ"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> mili giây"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> mili giây"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Đề xuất tên liên hệ"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Dấu cách đôi"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Chế độ nghiên cứu tính khả dụng"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Cài đặt thời gian rung khi nhấn phím"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Cài đặt âm lượng khi nhấn phím"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Đọc tệp từ điển bên ngoài"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Mặc định"</string>
</resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 922fcc6..b4ead85 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"弹出字符隐藏延迟"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"无延迟"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>毫秒"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>毫秒"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"联系人姓名建议"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用联系人中的姓名提供建议和更正"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"双击空格插入句号"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"可用性研究模式"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"按键振动持续时间设置"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"按键音量设置"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"读取外部字典文件"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"默认"</string>
</resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 1d16277..8d34ba2 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"關閉彈出式鍵盤的延遲時間"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"不延遲"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g> 毫秒"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> 毫秒"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"建議聯絡人名稱"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"根據「聯絡人」名稱提供建議與修正"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"輕按兩下空格鍵即插入句號"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"使用習慣學習模式"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"按鍵震動持續時間設定"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"按鍵音量設定"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"讀取外部字典檔案"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"預設"</string>
</resources>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 4914513..660dab1 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -42,7 +42,7 @@
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ukuvela kokhiye cashisa ukulibazisa"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Cha ukulibazisa"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Okuzenzakalelayo"</string>
- <string name="settings_keypress_vibration_duration" msgid="489402970497503329">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
+ <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sikisela amagama Othintana nabo"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Amagama abasebenzisi kusuka Kothintana nabo bokusikisela nokulungisa"</string>
<string name="use_double_space_period" msgid="8781529969425082860">"Isikhathi se-Double-space"</string>
@@ -146,4 +146,14 @@
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Imodi yesitadi yokusebenziseka"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Izilungiselelo ze-keypress vibration duraton"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Izilungiselelo zevolumu yomsindo wekeypress"</string>
+ <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Funda ifayela elangaphandle lesichazamazwi"</string>
+ <!-- no translation found for read_external_dictionary_no_files_message (4947420942224623792) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_multiple_files_title (7637749044265808628) -->
+ <skip />
+ <!-- no translation found for read_external_dictionary_confirm_install_message (6898610163768980870) -->
+ <skip />
+ <!-- no translation found for error (8940763624668513648) -->
+ <skip />
+ <string name="button_default" msgid="3988017840431881491">"Okuzenzakalelayo"</string>
</resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index e10bdb1..850b1b8 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -87,10 +87,8 @@
<attr name="keyRepeatStartTimeout" format="integer" />
<!-- Key repeat interval in millisecond. -->
<attr name="keyRepeatInterval" format="integer" />
- <!-- Long press timeout of letter key in millisecond. -->
- <attr name="longPressKeyTimeout" format="integer" />
- <!-- Long press timeout of shift key in millisecond. -->
- <attr name="longPressShiftKeyTimeout" format="integer" />
+ <!-- Long press timeout of shift key to shift lock in millisecond. -->
+ <attr name="longPressShiftLockTimeout" format="integer" />
<!-- Ignore special key timeout while typing in millisecond. -->
<attr name="ignoreAltCodeKeyTimeout" format="integer" />
<!-- Layout resource for key press feedback.-->
@@ -435,5 +433,7 @@
<declare-styleable name="SeekBarDialogPreference">
<attr name="valueFormatText" format="reference" />
<attr name="maxValue" format="integer" />
+ <attr name="minValue" format="integer" />
+ <attr name="stepValue" format="integer" />
</declare-styleable>
</resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 503e923..6b3c891 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -64,10 +64,13 @@
<bool name="config_sliding_key_input_enabled">true</bool>
<integer name="config_key_repeat_start_timeout">400</integer>
<integer name="config_key_repeat_interval">50</integer>
- <integer name="config_long_press_key_timeout">400</integer>
+ <integer name="config_default_longpress_key_timeout">300</integer> <!-- milliseconds -->
+ <integer name="config_longpress_timeout_step">10</integer> <!-- milliseconds -->
+ <integer name="config_min_longpress_timeout">100</integer> <!-- milliseconds -->
+ <integer name="config_max_longpress_timeout">700</integer> <!-- milliseconds -->
<!-- Long pressing shift will invoke caps-lock if > 0, never invoke caps-lock if == 0 -->
- <integer name="config_long_press_shift_key_timeout">1200</integer>
- <integer name="config_ignore_alt_code_key_timeout">350</integer>
+ <integer name="config_longpress_shift_lock_timeout">1200</integer> <!-- milliseconds -->
+ <integer name="config_ignore_alt_code_key_timeout">350</integer> <!-- milliseconds -->
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
false -->
<bool name="config_show_more_keys_keyboard_at_touched_point">false</bool>
diff --git a/java/res/values/research_strings.xml b/java/res/values/research_strings.xml
index 2cad15e..2d9dad1 100644
--- a/java/res/values/research_strings.xml
+++ b/java/res/values/research_strings.xml
@@ -21,4 +21,6 @@
<!-- Contents of note explaining what data is collected and how. -->
<!-- TODO: remove translatable=false attribute once text is stable -->
<string name="research_splash_content" translatable="false"></string>
+ <string name="research_account_type" translatable="false"></string>
+ <string name="research_allowed_account_domain" translatable="false"></string>
</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 54ff23b..e39cafc 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -270,6 +270,9 @@
<!-- Text for checkbox option to include user data in feedback for research purposes [CHAR LIMIT=50] -->
<!-- TODO: remove translatable=false attribute once text is stable -->
<string name="research_feedback_include_history_label" translatable="false">Include session history</string>
+ <!-- Text for checkbox option to include user account name in feedback for research purposes [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_include_account_name_label" translatable="false">Include account name</string>
<!-- Hint to user about the text entry field where they should enter research feedback [CHAR LIMIT=40] -->
<!-- TODO: remove translatable=false attribute once text is stable -->
<string name="research_feedback_hint" translatable="false">Enter your feedback here.</string>
@@ -371,6 +374,8 @@
<!-- Title of an option for usability study mode -->
<string name="prefs_usability_study_mode">Usability study mode</string>
+ <!-- Title of the settings for key long press delay -->
+ <string name="prefs_key_longpress_timeout_settings">Key long press delay settings</string>
<!-- Title of the settings for keypress vibration duration -->
<string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration settings</string>
<!-- Title of the settings for keypress sound volume -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index c398b59..f71963a 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -80,8 +80,7 @@
<item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item>
<item name="keyRepeatStartTimeout">@integer/config_key_repeat_start_timeout</item>
<item name="keyRepeatInterval">@integer/config_key_repeat_interval</item>
- <item name="longPressKeyTimeout">@integer/config_long_press_key_timeout</item>
- <item name="longPressShiftKeyTimeout">@integer/config_long_press_shift_key_timeout</item>
+ <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item>
<item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item>
<item name="keyPreviewLayout">@layout/key_preview</item>
<item name="keyPreviewOffset">@dimen/key_preview_offset</item>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index cc1b52b..84e7f54 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -146,6 +146,13 @@
android:key="pref_key_preview_popup_dismiss_delay"
android:title="@string/key_preview_popup_dismiss_delay" />
<com.android.inputmethod.latin.SeekBarDialogPreference
+ android:key="pref_key_longpress_timeout"
+ android:title="@string/prefs_key_longpress_timeout_settings"
+ latin:valueFormatText="@string/abbreviation_unit_milliseconds"
+ latin:minValue="@integer/config_min_longpress_timeout"
+ latin:maxValue="@integer/config_max_longpress_timeout"
+ latin:stepValue="@integer/config_longpress_timeout_step" />
+ <com.android.inputmethod.latin.SeekBarDialogPreference
android:key="pref_vibration_duration_settings"
android:title="@string/prefs_keypress_vibration_duration_settings"
latin:valueFormatText="@string/abbreviation_unit_milliseconds"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 670564c..4dab50f 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -169,11 +169,11 @@
public void setKeyboard(final Keyboard keyboard) {
mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard);
- requestLayout();
- invalidateAllKeys();
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes);
+ invalidateAllKeys();
+ requestLayout();
}
/**
@@ -638,7 +638,6 @@
public void closing() {
mInvalidateAllKeys = true;
mKeyboard = null;
- requestLayout();
}
@Override
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index d8ff5c2..cf89ef2 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -65,6 +65,7 @@
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.ResourceUtils;
+import com.android.inputmethod.latin.Settings;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SubtypeLocale;
@@ -174,9 +175,9 @@
// More keys keyboard
private final Paint mBackgroundDimAlphaPaint = new Paint();
private boolean mNeedsToDimEntireKeyboard;
- private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
- new WeakHashMap<Key, MoreKeysPanel>();
- private final int mMoreKeysLayout;
+ private final View mMoreKeysKeyboardContainer;
+ private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache =
+ CollectionUtils.newWeakHashMap();
private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
// More keys panel (used by both more keys keyboard and more suggestions view)
// TODO: Consider extending to support multiple more keys panels
@@ -205,8 +206,7 @@
private final int mKeyRepeatStartTimeout;
private final int mKeyRepeatInterval;
- private final int mLongPressKeyTimeout;
- private final int mLongPressShiftKeyTimeout;
+ private final int mLongPressShiftLockTimeout;
private final int mIgnoreAltCodeKeyTimeout;
private final int mGestureRecognitionUpdateTime;
@@ -218,10 +218,8 @@
R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_keyRepeatInterval, 0);
- mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
- mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
+ mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
@@ -245,7 +243,7 @@
break;
case MSG_LONGPRESS_KEY:
if (tracker != null) {
- keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker);
+ keyboardView.onLongPress(tracker);
} else {
KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
}
@@ -285,7 +283,7 @@
final int delay;
switch (code) {
case Constants.CODE_SHIFT:
- delay = mLongPressShiftKeyTimeout;
+ delay = mLongPressShiftLockTimeout;
break;
default:
delay = 0;
@@ -306,15 +304,17 @@
final int delay;
switch (key.mCode) {
case Constants.CODE_SHIFT:
- delay = mLongPressShiftKeyTimeout;
+ delay = mLongPressShiftLockTimeout;
break;
default:
+ final int longpressTimeout =
+ Settings.getInstance().getCurrent().mKeyLongpressTimeout;
if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
// We use longer timeout for sliding finger input started from the symbols
// mode key.
- delay = mLongPressKeyTimeout * 3;
+ delay = longpressTimeout * 3;
} else {
- delay = mLongPressKeyTimeout;
+ delay = longpressTimeout;
}
break;
}
@@ -544,7 +544,7 @@
if (mKeyPreviewLayoutId == 0) {
mShowKeyPreviewPopup = false;
}
- mMoreKeysLayout = mainKeyboardViewAttr.getResourceId(
+ final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
@@ -566,6 +566,8 @@
mPreviewPlacerView.addPreview(mSlidingKeyInputPreview);
mainKeyboardViewAttr.recycle();
+ mMoreKeysKeyboardContainer = LayoutInflater.from(getContext())
+ .inflate(moreKeysKeyboardLayoutId, null);
mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
languageOnSpacebarFadeoutAnimatorResId, this);
mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
@@ -653,7 +655,7 @@
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
PointerTracker.setKeyDetector(mKeyDetector);
mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth);
- mMoreKeysPanelCache.clear();
+ mMoreKeysKeyboardCache.clear();
mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
mSpaceIcon = (mSpaceKey != null)
@@ -942,123 +944,105 @@
}
}
- private boolean openMoreKeysKeyboardIfRequired(final Key parentKey,
- final PointerTracker tracker) {
- // Check if we have a popup layout specified first.
- if (mMoreKeysLayout == 0) {
- return false;
- }
-
- // Check if we are already displaying popup panel.
- if (mMoreKeysPanel != null) {
- return false;
- }
- if (parentKey == null) {
- return false;
- }
- return onLongPress(parentKey, tracker);
- }
-
- private MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
- if (parentKey.mMoreKeys == null) {
+ private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
+ if (key.mMoreKeys == null) {
return null;
}
-
- final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
- if (container == null) {
- throw new NullPointerException();
+ Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
+ if (moreKeysKeyboard == null) {
+ moreKeysKeyboard = new MoreKeysKeyboard.Builder(
+ context, key, this, mKeyPreviewDrawParams).build();
+ mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
}
+ final View container = mMoreKeysKeyboardContainer;
final MoreKeysKeyboardView moreKeysKeyboardView =
(MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
- final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(
- container, parentKey, this, mKeyPreviewDrawParams)
- .build();
moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
return moreKeysKeyboardView;
}
/**
* Called when a key is long pressed.
- * @param parentKey the key that was long pressed
* @param tracker the pointer tracker which pressed the parent key
* @return true if the long press is handled, false otherwise. Subclasses should call the
* method on the base class if the subclass doesn't wish to handle the call.
*/
- private boolean onLongPress(final Key parentKey, final PointerTracker tracker) {
+ private boolean onLongPress(final PointerTracker tracker) {
+ if (isShowingMoreKeysPanel()) {
+ return false;
+ }
+ final Key key = tracker.getKey();
+ if (key == null) {
+ return false;
+ }
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.mainKeyboardView_onLongPress();
}
- final int primaryCode = parentKey.mCode;
- if (parentKey.hasEmbeddedMoreKey()) {
- final int embeddedCode = parentKey.mMoreKeys[0].mCode;
+ final int code = key.mCode;
+ if (key.hasEmbeddedMoreKey()) {
+ final int embeddedCode = key.mMoreKeys[0].mCode;
tracker.onLongPressed();
invokeCodeInput(embeddedCode);
- invokeReleaseKey(primaryCode);
- KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
+ invokeReleaseKey(code);
+ KeyboardSwitcher.getInstance().hapticAndAudioFeedback(code);
return true;
}
- if (primaryCode == Constants.CODE_SPACE || primaryCode == Constants.CODE_LANGUAGE_SWITCH) {
+ if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
// Long pressing the space key invokes IME switcher dialog.
if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
tracker.onLongPressed();
- invokeReleaseKey(primaryCode);
+ invokeReleaseKey(code);
return true;
}
}
- return openMoreKeysPanel(parentKey, tracker);
+ return openMoreKeysPanel(key, tracker);
}
- private boolean invokeCustomRequest(final int code) {
- return mKeyboardActionListener.onCustomRequest(code);
+ private boolean invokeCustomRequest(final int requestCode) {
+ return mKeyboardActionListener.onCustomRequest(requestCode);
}
- private void invokeCodeInput(final int primaryCode) {
+ private void invokeCodeInput(final int code) {
mKeyboardActionListener.onCodeInput(
- primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
- private void invokeReleaseKey(final int primaryCode) {
- mKeyboardActionListener.onReleaseKey(primaryCode, false);
+ private void invokeReleaseKey(final int code) {
+ mKeyboardActionListener.onReleaseKey(code, false);
}
- private boolean openMoreKeysPanel(final Key parentKey, final PointerTracker tracker) {
- MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
+ private boolean openMoreKeysPanel(final Key key, final PointerTracker tracker) {
+ final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext());
if (moreKeysPanel == null) {
- moreKeysPanel = onCreateMoreKeysPanel(parentKey);
- if (moreKeysPanel == null) {
- return false;
- }
- mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
+ return false;
}
final int[] lastCoords = CoordinateUtils.newInstance();
tracker.getLastCoordinates(lastCoords);
- final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !parentKey.noKeyPreview();
+ final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview();
// The more keys keyboard is usually horizontally aligned with the center of the parent key.
// If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
// keys keyboard is placed at the touch point of the parent key.
final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
? CoordinateUtils.x(lastCoords)
- : parentKey.mX + parentKey.mWidth / 2;
+ : key.mX + key.mWidth / 2;
// The more keys keyboard is usually vertically aligned with the top edge of the parent key
// (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
// aligned with the bottom edge of the visible part of the key preview.
// {@code mPreviewVisibleOffset} has been set appropriately in
// {@link KeyboardView#showKeyPreview(PointerTracker)}.
- final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
+ final int pointY = key.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords));
final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords));
tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
- dimEntireKeyboard(true /* dimmed */);
return true;
}
public boolean isInSlidingKeyInput() {
- if (mMoreKeysPanel != null) {
+ if (isShowingMoreKeysPanel()) {
return true;
}
return PointerTracker.isAnyInSlidingKeyInput();
@@ -1069,19 +1053,17 @@
if (isShowingMoreKeysPanel()) {
onDismissMoreKeysPanel();
}
+ mPreviewPlacerView.addView(panel.getContainerView());
mMoreKeysPanel = panel;
- mPreviewPlacerView.addView(mMoreKeysPanel.getContainerView());
+ dimEntireKeyboard(true /* dimmed */);
}
public boolean isShowingMoreKeysPanel() {
- return (mMoreKeysPanel != null);
+ return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent();
}
@Override
public void onCancelMoreKeysPanel() {
- if (isShowingMoreKeysPanel()) {
- mMoreKeysPanel.dismissMoreKeysPanel();
- }
PointerTracker.dismissAllMoreKeysPanels();
}
@@ -1254,9 +1236,9 @@
public void closing() {
dismissAllKeyPreviews();
cancelAllMessages();
+ onDismissMoreKeysPanel();
+ mMoreKeysKeyboardCache.clear();
super.closing();
- onCancelMoreKeysPanel();
- mMoreKeysPanelCache.clear();
}
/**
@@ -1331,7 +1313,7 @@
invalidateKey(mSpaceKey);
}
- public void dimEntireKeyboard(final boolean dimmed) {
+ private void dimEntireKeyboard(final boolean dimmed) {
final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
mNeedsToDimEntireKeyboard = dimmed;
if (needsRedrawing) {
@@ -1389,7 +1371,7 @@
private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
final int width) {
// Choose appropriate language name to fit into the width.
- final String fullText = getFullDisplayName(subtype, getResources());
+ final String fullText = getFullDisplayName(subtype);
if (fitsTextIntoWidth(width, fullText, paint)) {
return fullText;
}
@@ -1463,12 +1445,12 @@
// zz azerty T AZERTY AZERTY
// Get InputMethodSubtype's full display name in its locale.
- static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
+ static String getFullDisplayName(final InputMethodSubtype subtype) {
if (SubtypeLocale.isNoLanguage(subtype)) {
return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
}
- return SubtypeLocale.getSubtypeDisplayName(subtype, res);
+ return SubtypeLocale.getSubtypeDisplayName(subtype);
}
// Get InputMethodSubtype's short display name in its locale.
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index 6df883e..66c3014 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -16,9 +16,9 @@
package com.android.inputmethod.keyboard;
+import android.content.Context;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
-import android.view.View;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
@@ -260,15 +260,15 @@
/**
* The builder of MoreKeysKeyboard.
- * @param containerView the container of {@link MoreKeysKeyboardView}.
+ * @param context the context of {@link MoreKeysKeyboardView}.
* @param parentKey the {@link Key} that invokes more keys keyboard.
* @param parentKeyboardView the {@link KeyboardView} that contains the parentKey.
* @param keyPreviewDrawParams the parameter to place key preview.
*/
- public Builder(final View containerView, final Key parentKey,
+ public Builder(final Context context, final Key parentKey,
final MainKeyboardView parentKeyboardView,
final KeyPreviewDrawParams keyPreviewDrawParams) {
- super(containerView.getContext(), new MoreKeysKeyboardParams());
+ super(context, new MoreKeysKeyboardParams());
final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
load(parentKeyboard.mMoreKeysTemplate, parentKeyboard.mId);
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 0d42ab2..9e75f8b 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -174,6 +174,7 @@
@Override
public boolean dismissMoreKeysPanel() {
+ super.closing();
if (mController == null) return false;
return mController.onDismissMoreKeysPanel();
}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 29c65f1..32cee73 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -30,8 +30,7 @@
private static final String TAG = ProximityInfo.class.getSimpleName();
private static final boolean DEBUG = false;
- /** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL
- * in defines.h */
+ // Must be equal to MAX_PROXIMITY_CHARS_SIZE in native/jni/src/defines.h
public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
/** Number of key widths from current touch point to search for nearest keys. */
private static final float SEARCH_DISTANCE = 1.2f;
@@ -88,9 +87,13 @@
final int rowSize, final int gridWidth, final int gridHeight) {
final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
spellCheckerProximityInfo.mNativeProximityInfo =
- spellCheckerProximityInfo.setProximityInfoNative("",
- rowSize, gridWidth, gridHeight, gridWidth, gridHeight,
- 1, proximityCharsArray, 0, null, null, null, null, null, null, null, null);
+ spellCheckerProximityInfo.setProximityInfoNative("" /* locale */,
+ gridWidth /* displayWidth */, gridHeight /* displayHeight */,
+ gridWidth, gridHeight, 1 /* mostCommonKeyWidth */, proximityCharsArray,
+ 0 /* keyCount */, null /*keyXCoordinates */, null /* keyYCoordinates */,
+ null /* keyWidths */, null /* keyHeights */, null /* keyCharCodes */,
+ null /* sweetSpotCenterXs */, null /* sweetSpotCenterYs */,
+ null /* sweetSpotRadii */);
return spellCheckerProximityInfo;
}
@@ -100,11 +103,11 @@
}
// TODO: Stop passing proximityCharsArray
- private static native long setProximityInfoNative(String locale, int maxProximityCharsSize,
+ private static native long setProximityInfoNative(String locale,
int displayWidth, int displayHeight, int gridWidth, int gridHeight,
int mostCommonKeyWidth, int[] proximityCharsArray, int keyCount, int[] keyXCoordinates,
int[] keyYCoordinates, int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
- float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
+ float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii);
private static native void releaseProximityInfoNative(long nativeProximityInfo);
@@ -230,9 +233,9 @@
}
// TODO: Stop passing proximityCharsArray
- return setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE, mKeyboardMinWidth,
- mKeyboardHeight, mGridWidth, mGridHeight, mMostCommonKeyWidth, proximityCharsArray,
- keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
+ return setProximityInfoNative(mLocaleStr, mKeyboardMinWidth, mKeyboardHeight,
+ mGridWidth, mGridHeight, mMostCommonKeyWidth, proximityCharsArray, keyCount,
+ keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
}
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 9e5e183..a56c78b 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -103,7 +103,7 @@
if (DEBUG_SUBTYPE_ID) {
android.util.Log.d(TAG, String.format("%-6s 0x%08x %11d %s",
subtype.getLocale(), subtype.hashCode(), subtype.hashCode(),
- SubtypeLocale.getSubtypeDisplayName(subtype, context.getResources())));
+ SubtypeLocale.getSubtypeDisplayName(subtype)));
}
if (subtype.containsExtraValueKey(ASCII_CAPABLE)) {
items.add(createItem(context, subtype.getLocale()));
@@ -205,8 +205,7 @@
setDialogTitle(R.string.add_style);
setKey(KEY_NEW_SUBTYPE);
} else {
- final String displayName = SubtypeLocale.getSubtypeDisplayName(
- subtype, getContext().getResources());
+ final String displayName = SubtypeLocale.getSubtypeDisplayName(subtype);
setTitle(displayName);
setDialogTitle(displayName);
setKey(KEY_PREFIX + subtype.getLocale() + "_"
@@ -498,7 +497,7 @@
final Context context = getActivity();
final Resources res = context.getResources();
final String message = res.getString(R.string.custom_input_style_already_exists,
- SubtypeLocale.getSubtypeDisplayName(subtype, res));
+ SubtypeLocale.getSubtypeDisplayName(subtype));
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 59d51b0..ad31633 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -35,9 +35,9 @@
public static final String DICTIONARY_PACK_AUTHORITY =
"com.android.inputmethod.latin.dictionarypack";
- // Must be identical to MAX_WORD_LENGTH in native/jni/src/defines.h
+ // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
private static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH;
- // Must be identical to MAX_RESULTS in native/jni/src/defines.h
+ // Must be equal to MAX_RESULTS in native/jni/src/defines.h
private static final int MAX_RESULTS = 18;
private long mNativeDict;
diff --git a/java/src/com/android/inputmethod/latin/CollectionUtils.java b/java/src/com/android/inputmethod/latin/CollectionUtils.java
index c75f2df..a8623cc 100644
--- a/java/src/com/android/inputmethod/latin/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/CollectionUtils.java
@@ -27,6 +27,7 @@
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -39,6 +40,10 @@
return new HashMap<K,V>();
}
+ public static <K, V> WeakHashMap<K, V> newWeakHashMap() {
+ return new WeakHashMap<K, V>();
+ }
+
public static <K,V> TreeMap<K,V> newTreeMap() {
return new TreeMap<K,V>();
}
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 748900b..85cc552 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -127,7 +127,7 @@
}
public static final class Dictionary {
- // Must be identical to MAX_WORD_LENGTH in native/jni/src/defines.h
+ // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
public static final int MAX_WORD_LENGTH = 48;
private Dictionary() {
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 4d6c4f3..81c8330 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -18,8 +18,11 @@
import com.android.inputmethod.annotations.UsedForTesting;
+import android.util.Log;
+
// TODO: This class is not thread-safe.
public final class InputPointers {
+ private static final String TAG = InputPointers.class.getSimpleName();
private final int mDefaultCapacity;
private final ResizableIntArray mXCoordinates;
private final ResizableIntArray mYCoordinates;
@@ -126,6 +129,11 @@
}
public int[] getTimes() {
+ if (LatinImeLogger.sDBG) {
+ if (!isValidTimeStamps()) {
+ throw new RuntimeException("Time stamps are invalid.");
+ }
+ }
return mTimes.getPrimitiveArray();
}
@@ -134,4 +142,18 @@
return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes
+ " x=" + mXCoordinates + " y=" + mYCoordinates;
}
+
+ private boolean isValidTimeStamps() {
+ final int[] times = mTimes.getPrimitiveArray();
+ for (int i = 1; i < getPointerSize(); ++i) {
+ if (times[i] < times[i - 1]) {
+ // dump
+ for (int j = 0; j < times.length; ++j) {
+ Log.d(TAG, "--- (" + j + ") " + times[j]);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6085cb4..70eb6e6 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2491,7 +2491,9 @@
private void launchSubActivity(final Class<? extends Activity> activityClass) {
Intent intent = new Intent();
intent.setClass(LatinIME.this, activityClass);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
index 6041564..9819a02 100644
--- a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
+++ b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java
@@ -37,6 +37,8 @@
private final int mValueFormatResId;
private final int mMaxValue;
+ private final int mMinValue;
+ private final int mStepValue;
private TextView mValueView;
private SeekBar mSeekBar;
@@ -49,6 +51,8 @@
attrs, R.styleable.SeekBarDialogPreference, 0, 0);
mValueFormatResId = a.getResourceId(R.styleable.SeekBarDialogPreference_valueFormatText, 0);
mMaxValue = a.getInt(R.styleable.SeekBarDialogPreference_maxValue, 0);
+ mMinValue = a.getInt(R.styleable.SeekBarDialogPreference_minValue, 0);
+ mStepValue = a.getInt(R.styleable.SeekBarDialogPreference_stepValue, 0);
a.recycle();
setDialogLayoutResource(R.layout.seek_bar_dialog);
}
@@ -70,22 +74,42 @@
protected View onCreateDialogView() {
final View view = super.onCreateDialogView();
mSeekBar = (SeekBar)view.findViewById(R.id.seek_bar_dialog_bar);
- mSeekBar.setMax(mMaxValue);
+ mSeekBar.setMax(mMaxValue - mMinValue);
mSeekBar.setOnSeekBarChangeListener(this);
mValueView = (TextView)view.findViewById(R.id.seek_bar_dialog_value);
return view;
}
+ private int getProgressFromValue(final int value) {
+ return value - mMinValue;
+ }
+
+ private int getValueFromProgress(final int progress) {
+ return progress + mMinValue;
+ }
+
+ private int clipValue(final int value) {
+ final int clippedValue = Math.min(mMaxValue, Math.max(mMinValue, value));
+ if (mStepValue <= 1) {
+ return clippedValue;
+ }
+ return clippedValue - (clippedValue % mStepValue);
+ }
+
+ private int getClippedValueFromProgress(final int progress) {
+ return clipValue(getValueFromProgress(progress));
+ }
+
private void setValue(final int value, final boolean fromUser) {
mValueView.setText(getValueText(value));
if (!fromUser) {
- mSeekBar.setProgress(value);
+ mSeekBar.setProgress(getProgressFromValue(value));
}
}
@Override
protected void onBindDialogView(final View view) {
- setValue(mValueProxy.readValue(getKey()), false /* fromUser */);
+ setValue(clipValue(mValueProxy.readValue(getKey())), false /* fromUser */);
}
@Override
@@ -99,18 +123,18 @@
public void onClick(final DialogInterface dialog, final int which) {
super.onClick(dialog, which);
if (which == DialogInterface.BUTTON_NEUTRAL) {
- setValue(mValueProxy.readDefaultValue(getKey()), false /* fromUser */);
+ setValue(clipValue(mValueProxy.readDefaultValue(getKey())), false /* fromUser */);
}
if (which != DialogInterface.BUTTON_NEGATIVE) {
setSummary(mValueView.getText());
- mValueProxy.writeValue(mSeekBar.getProgress(), getKey());
+ mValueProxy.writeValue(getClippedValueFromProgress(mSeekBar.getProgress()), getKey());
}
}
@Override
public void onProgressChanged(final SeekBar seekBar, final int progress,
final boolean fromUser) {
- setValue(progress, fromUser);
+ setValue(getClippedValueFromProgress(progress), fromUser);
}
@Override
@@ -118,6 +142,6 @@
@Override
public void onStopTrackingTouch(final SeekBar seekBar) {
- mValueProxy.feedbackValue(seekBar.getProgress());
+ mValueProxy.feedbackValue(getClippedValueFromProgress(seekBar.getProgress()));
}
}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index aa705da..408ea4a 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -55,6 +55,7 @@
public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
public static final String PREF_GESTURE_SETTINGS = "gesture_typing_settings";
public static final String PREF_GESTURE_INPUT = "gesture_input";
+ public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
public static final String PREF_VIBRATION_DURATION_SETTINGS =
"pref_vibration_duration_settings";
public static final String PREF_KEYPRESS_SOUND_VOLUME =
@@ -175,6 +176,16 @@
ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_volumes));
}
+ public static int readKeyLongpressTimeout(final SharedPreferences prefs,
+ final Resources res) {
+ final int ms = prefs.getInt(PREF_KEY_LONGPRESS_TIMEOUT, -1);
+ return (ms >= 0) ? ms : readDefaultKeyLongpressTimeout(res);
+ }
+
+ public static int readDefaultKeyLongpressTimeout(final Resources res) {
+ return res.getInteger(R.integer.config_default_longpress_key_timeout);
+ }
+
public static int readKeypressVibrationDuration(final SharedPreferences prefs,
final Resources res) {
final int ms = prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, -1);
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index afe6f35..3ba24fb 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -164,6 +164,7 @@
getPreferenceScreen().removePreference(gestureTypingSettings);
}
+ setupKeyLongpressTimeoutSettings(prefs, res);
setupKeypressVibrationDurationSettings(prefs, res);
setupKeypressSoundVolumeSettings(prefs, res);
refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
@@ -236,7 +237,7 @@
final StringBuilder styles = new StringBuilder();
for (final InputMethodSubtype subtype : subtypes) {
if (styles.length() > 0) styles.append(", ");
- styles.append(SubtypeLocale.getSubtypeDisplayName(subtype, res));
+ styles.append(SubtypeLocale.getSubtypeDisplayName(subtype));
}
customInputStyles.setSummary(styles);
}
@@ -298,6 +299,34 @@
});
}
+ private void setupKeyLongpressTimeoutSettings(final SharedPreferences sp,
+ final Resources res) {
+ 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) {
+ sp.edit().putInt(key, value).apply();
+ }
+
+ @Override
+ public int readValue(final String key) {
+ return Settings.readKeyLongpressTimeout(sp, res);
+ }
+
+ @Override
+ public int readDefaultValue(final String key) {
+ return Settings.readDefaultKeyLongpressTimeout(res);
+ }
+
+ @Override
+ public void feedbackValue(final int value) {}
+ });
+ }
+
private void setupKeypressSoundVolumeSettings(final SharedPreferences sp, final Resources res) {
final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
Settings.PREF_KEYPRESS_SOUND_VOLUME);
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index d6556d6..29e79e4 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -59,6 +59,7 @@
public final boolean mGestureInputEnabled;
public final boolean mGesturePreviewTrailEnabled;
public final boolean mGestureFloatingPreviewTextEnabled;
+ public final int mKeyLongpressTimeout;
// From the input box
public final InputAttributes mInputAttributes;
@@ -121,6 +122,7 @@
mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
// Compute other readable settings
+ mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res);
mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 370a659..068c34e 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -42,6 +42,7 @@
public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic;
private static boolean sInitialized = false;
+ private static Resources sResources;
private static String[] sPredefinedKeyboardLayoutSet;
// Keyboard layout to its display name map.
private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap =
@@ -71,10 +72,11 @@
}
// Note that this initialization method can be called multiple times.
- public static synchronized void init(Context context) {
+ public static synchronized void init(final Context context) {
if (sInitialized) return;
final Resources res = context.getResources();
+ sResources = res;
final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
@@ -121,15 +123,15 @@
return sPredefinedKeyboardLayoutSet;
}
- public static boolean isExceptionalLocale(String localeString) {
+ public static boolean isExceptionalLocale(final String localeString) {
return sExceptionalLocaleToWithLayoutNameIdsMap.containsKey(localeString);
}
- private static final String getNoLanguageLayoutKey(String keyboardLayoutName) {
+ private static final String getNoLanguageLayoutKey(final String keyboardLayoutName) {
return NO_LANGUAGE + "_" + keyboardLayoutName;
}
- public static int getSubtypeNameId(String localeString, String keyboardLayoutName) {
+ public static int getSubtypeNameId(final String localeString, final String keyboardLayoutName) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& isExceptionalLocale(localeString)) {
return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString);
@@ -141,7 +143,7 @@
return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId;
}
- public static String getSubtypeLocaleDisplayName(String localeString) {
+ public static String getSubtypeLocaleDisplayName(final String localeString) {
final String exceptionalValue = sExceptionalDisplayNamesMap.get(localeString);
if (exceptionalValue != null) {
return exceptionalValue;
@@ -166,12 +168,17 @@
// en_US azerty T English (US) (AZERTY)
// zz azerty T No language (AZERTY) in system locale
- public static String getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res) {
- final String replacementString =
- (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
- && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME))
- ? subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
- : getSubtypeLocaleDisplayName(subtype.getLocale());
+ private static String getReplacementString(final InputMethodSubtype subtype) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
+ return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
+ } else {
+ return getSubtypeLocaleDisplayName(subtype.getLocale());
+ }
+ }
+
+ public static String getSubtypeDisplayName(final InputMethodSubtype subtype) {
+ final String replacementString = getReplacementString(subtype);
final int nameResId = subtype.getNameResId();
final RunInLocale<String> getSubtypeName = new RunInLocale<String>() {
@Override
@@ -190,30 +197,30 @@
}
};
final Locale locale = isNoLanguage(subtype)
- ? res.getConfiguration().locale : getSubtypeLocale(subtype);
- return getSubtypeName.runInLocale(res, locale);
+ ? sResources.getConfiguration().locale : getSubtypeLocale(subtype);
+ return getSubtypeName.runInLocale(sResources, locale);
}
- public static boolean isNoLanguage(InputMethodSubtype subtype) {
+ public static boolean isNoLanguage(final InputMethodSubtype subtype) {
final String localeString = subtype.getLocale();
return localeString.equals(NO_LANGUAGE);
}
- public static Locale getSubtypeLocale(InputMethodSubtype subtype) {
+ public static Locale getSubtypeLocale(final InputMethodSubtype subtype) {
final String localeString = subtype.getLocale();
return LocaleUtils.constructLocaleFromString(localeString);
}
- public static String getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype) {
+ public static String getKeyboardLayoutSetDisplayName(final InputMethodSubtype subtype) {
final String layoutName = getKeyboardLayoutSetName(subtype);
return getKeyboardLayoutSetDisplayName(layoutName);
}
- public static String getKeyboardLayoutSetDisplayName(String layoutName) {
+ public static String getKeyboardLayoutSetDisplayName(final String layoutName) {
return sKeyboardLayoutToDisplayNameMap.get(layoutName);
}
- public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
+ public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) {
String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
if (keyboardLayoutSet == null) {
// This subtype doesn't have a keyboardLayoutSet extra value, so lookup its keyboard
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index eb0ec39..2f9e34f 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -151,8 +151,7 @@
// Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
public void onSubtypeChanged(final InputMethodSubtype newSubtype) {
if (DBG) {
- Log.w(TAG, "onSubtypeChanged: " + SubtypeLocale.getSubtypeDisplayName(
- newSubtype, mResources));
+ Log.w(TAG, "onSubtypeChanged: " + SubtypeLocale.getSubtypeDisplayName(newSubtype));
}
final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 26a304e..438820d 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.util.AttributeSet;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
import com.android.inputmethod.latin.R;
@@ -28,7 +27,6 @@
* key presses and touch movements.
*/
public final class MoreSuggestionsView extends MoreKeysKeyboardView {
-
public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.moreSuggestionsViewStyle);
}
@@ -44,32 +42,15 @@
return pane.mOccupiedWidth / 2;
}
- @Override
- protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard != null) {
- final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
- final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
- setMeasuredDimension(width, height);
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
public void updateKeyboardGeometry(final int keyHeight) {
mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
}
@Override
- public void onCodeInput(final int primaryCode, final int x, final int y) {
- final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
+ public void onCodeInput(final int code, final int x, final int y) {
+ final int index = code - MoreSuggestions.SUGGESTION_CODE_BASE;
if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
mListener.onCustomRequest(index);
}
}
-
- @Override
- public boolean isShowingInParent() {
- return (getContainerView().getParent() != null);
- }
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 92b96e7..bc51d5d 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -676,12 +676,11 @@
new MoreKeysPanel.Controller() {
@Override
public boolean onDismissMoreKeysPanel() {
- mMainKeyboardView.dimEntireKeyboard(false /* dimmed */);
return mMainKeyboardView.onDismissMoreKeysPanel();
}
@Override
- public void onShowMoreKeysPanel(MoreKeysPanel panel) {
+ public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
mMainKeyboardView.onShowMoreKeysPanel(panel);
}
@@ -728,7 +727,6 @@
mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
mOriginX = mLastX;
mOriginY = mLastY;
- mMainKeyboardView.dimEntireKeyboard(true /* dimmed */);
for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
mWords.get(i).setPressed(false);
}
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
index a9adbb7..fee61a9 100644
--- a/java/src/com/android/inputmethod/research/FeedbackFragment.java
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -32,7 +32,8 @@
public class FeedbackFragment extends Fragment {
private EditText mEditText;
- private CheckBox mCheckBox;
+ private CheckBox mIncludingHistoryCheckBox;
+ private CheckBox mIncludingAccountNameCheckBox;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -40,7 +41,10 @@
final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container,
false);
mEditText = (EditText) view.findViewById(R.id.research_feedback_contents);
- mCheckBox = (CheckBox) view.findViewById(R.id.research_feedback_include_history);
+ mIncludingHistoryCheckBox = (CheckBox) view.findViewById(
+ R.id.research_feedback_include_history);
+ mIncludingAccountNameCheckBox = (CheckBox) view.findViewById(
+ R.id.research_feedback_include_account_name);
final Button sendButton = (Button) view.findViewById(
R.id.research_feedback_send_button);
@@ -49,8 +53,10 @@
public void onClick(View v) {
final Editable editable = mEditText.getText();
final String feedbackContents = editable.toString();
- final boolean includeHistory = mCheckBox.isChecked();
- ResearchLogger.getInstance().sendFeedback(feedbackContents, includeHistory);
+ final boolean isIncludingHistory = mIncludingHistoryCheckBox.isChecked();
+ final boolean isIncludingAccountName = mIncludingAccountNameCheckBox.isChecked();
+ ResearchLogger.getInstance().sendFeedback(feedbackContents, isIncludingHistory,
+ isIncludingAccountName);
final Activity activity = FeedbackFragment.this.getActivity();
activity.finish();
ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 29bc708..dbf2d29 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -18,6 +18,8 @@
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -30,6 +32,7 @@
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -115,6 +118,8 @@
private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid";
private static final ResearchLogger sInstance = new ResearchLogger();
+ private static String sAccountType = null;
+ private static String sAllowedAccountDomain = null;
// to write to a different filename, e.g., for testing, set mFile before calling start()
/* package */ File mFilesDir;
/* package */ String mUUIDString;
@@ -199,6 +204,9 @@
e.apply();
}
}
+ final Resources res = latinIME.getResources();
+ sAccountType = res.getString(R.string.research_account_type);
+ sAllowedAccountDomain = res.getString(R.string.research_allowed_account_domain);
mLatinIME = latinIME;
mPrefs = prefs;
mUploadIntent = new Intent(mLatinIME, UploaderService.class);
@@ -593,6 +601,36 @@
}
*/
+ /**
+ * Get the name of the first allowed account on the device.
+ *
+ * Allowed accounts must be in the domain given by ALLOWED_ACCOUNT_DOMAIN.
+ *
+ * @return The user's account name.
+ */
+ public String getAccountName() {
+ if (sAccountType == null || sAccountType.isEmpty()) {
+ return null;
+ }
+ if (sAllowedAccountDomain == null || sAllowedAccountDomain.isEmpty()) {
+ return null;
+ }
+ final AccountManager manager = AccountManager.get(mLatinIME);
+ // Filter first by account type.
+ final Account[] accounts = manager.getAccountsByType(sAccountType);
+
+ for (final Account account : accounts) {
+ if (DEBUG) {
+ Log.d(TAG, account.name);
+ }
+ final String[] parts = account.name.split("@");
+ if (parts.length > 1 && parts[1].equals(sAllowedAccountDomain)) {
+ return parts[0];
+ }
+ }
+ return null;
+ }
+
static class LogStatement {
final String mName;
@@ -626,8 +664,9 @@
}
private static final LogStatement LOGSTATEMENT_FEEDBACK =
- new LogStatement("UserFeedback", false, false, "contents");
- public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
+ new LogStatement("UserFeedback", false, false, "contents", "accountName");
+ public void sendFeedback(final String feedbackContents, final boolean includeHistory,
+ final boolean isIncludingAccountName) {
if (mSavedFeedbackLogBuffer == null) {
return;
}
@@ -635,8 +674,9 @@
mSavedFeedbackLogBuffer.clear();
}
final LogUnit feedbackLogUnit = new LogUnit();
+ final String accountName = isIncludingAccountName ? getAccountName() : "";
feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(),
- feedbackContents);
+ feedbackContents, accountName);
mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
publishLogBuffer(mFeedbackLogBuffer, mSavedFeedbackLog, true /* isIncludingPrivateData */);
mSavedFeedbackLog.close(new Runnable() {
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index f2aebd5..3735ec0 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -54,7 +54,9 @@
dictionary.cpp \
dic_traverse_wrapper.cpp \
proximity_info.cpp \
+ proximity_info_params.cpp \
proximity_info_state.cpp \
+ proximity_info_state_utils.cpp \
unigram_dictionary.cpp \
words_priority_queue.cpp \
suggest/gesture_suggest.cpp \
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index d718290..30ca3f1 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -25,14 +25,14 @@
namespace latinime {
static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jclass clazz, jstring localeJStr,
- jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth,
- jint gridHeight, jint mostCommonkeyWidth, jintArray proximityChars, jint keyCount,
+ jint displayWidth, jint displayHeight, jint gridWidth, jint gridHeight,
+ jint mostCommonkeyWidth, jintArray proximityChars, jint keyCount,
jintArray keyXCoordinates, jintArray keyYCoordinates, jintArray keyWidths,
jintArray keyHeights, jintArray keyCharCodes, jfloatArray sweetSpotCenterXs,
jfloatArray sweetSpotCenterYs, jfloatArray sweetSpotRadii) {
- ProximityInfo *proximityInfo = new ProximityInfo(env, localeJStr, maxProximityCharsSize,
- displayWidth, displayHeight, gridWidth, gridHeight, mostCommonkeyWidth, proximityChars,
- keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
+ ProximityInfo *proximityInfo = new ProximityInfo(env, localeJStr, displayWidth, displayHeight,
+ gridWidth, gridHeight, mostCommonkeyWidth, proximityChars, keyCount,
+ keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
return reinterpret_cast<jlong>(proximityInfo);
}
@@ -43,7 +43,7 @@
}
static JNINativeMethod sMethods[] = {
- {"setProximityInfoNative", "(Ljava/lang/String;IIIIII[II[I[I[I[I[I[F[F[F)J",
+ {"setProximityInfoNative", "(Ljava/lang/String;IIIII[II[I[I[I[I[I[F[F[F)J",
reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
{"releaseProximityInfoNative", "(J)V", reinterpret_cast<void *>(latinime_Keyboard_release)}
};
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index f5f5278..922a746 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -28,10 +28,13 @@
#define AK_FORCE_INLINE inline
#endif // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
-// Must be identical to Constants.Dictionary.MAX_WORD_LENGTH in Java
+// Must be equal to Constants.Dictionary.MAX_WORD_LENGTH in Java
#define MAX_WORD_LENGTH 48
-// Must be identical to BinaryDictionary.MAX_RESULTS in Java
+// Must be equal to BinaryDictionary.MAX_RESULTS in Java
#define MAX_RESULTS 18
+// Must be equal to ProximityInfo.MAX_PROXIMITY_CHARS_SIZE in Java
+#define MAX_PROXIMITY_CHARS_SIZE 16
+#define ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE 2
#if defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
#include <android/log.h>
@@ -248,6 +251,10 @@
// GCC warns about this.
#define S_INT_MIN (-2147483647 - 1) // -(1 << 31)
#endif
+
+#define M_PI_F 3.14159265f
+#define MAX_PERCENTILE 100
+
// Number of base-10 digits in the largest integer + 1 to leave room for a zero terminator.
// As such, this is the maximum number of characters will be needed to represent an int as a
// string, including the terminator; this is used as the size of a string buffer large enough to
@@ -322,12 +329,6 @@
#define MAX_FREQ 255
#define MAX_BIGRAM_FREQ 15
-// This must be the same as ProximityInfo#MAX_PROXIMITY_CHARS_SIZE, currently it's 16.
-#define MAX_PROXIMITY_CHARS_SIZE_INTERNAL 16
-
-// This must be equal to ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE in KeyDetector.java
-#define ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE 2
-
// Assuming locale strings such as en_US, sr-Latn etc.
#define MAX_LOCALE_STRING_LENGTH 10
@@ -392,8 +393,6 @@
template<typename T> inline T min(T a, T b) { return a < b ? a : b; }
template<typename T> inline T max(T a, T b) { return a > b ? a : b; }
-#define M_PI_F 3.14159265f
-
#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
// The ratio of neutral area radius to sweet spot radius.
diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp
index c563b07..8157fe2 100644
--- a/native/jni/src/proximity_info.cpp
+++ b/native/jni/src/proximity_info.cpp
@@ -47,15 +47,14 @@
}
}
-ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize,
+ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr,
const int keyboardWidth, const int keyboardHeight, const int gridWidth,
const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
const jfloatArray sweetSpotRadii)
- : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), GRID_WIDTH(gridWidth),
- GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth),
+ : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth),
MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
@@ -65,11 +64,17 @@
&& keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
&& sweetSpotCenterYs && sweetSpotRadii),
mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE
- /* proximityGridLength */]),
+ /* proximityCharsLength */]),
mCodeToKeyMap() {
- const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
+ /* Let's check the input array length here to make sure */
+ const jsize proximityCharsLength = env->GetArrayLength(proximityChars);
+ if (proximityCharsLength != GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE) {
+ AKLOGE("Invalid proximityCharsLength: %d", proximityCharsLength);
+ ASSERT(false);
+ return;
+ }
if (DEBUG_PROXIMITY_INFO) {
- AKLOGI("Create proximity info array %d", proximityGridLength);
+ AKLOGI("Create proximity info array %d", proximityCharsLength);
}
const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
@@ -78,7 +83,8 @@
}
memset(mLocaleStr, 0, sizeof(mLocaleStr));
env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
- safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
+ safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityCharsLength,
+ mProximityCharsArray);
safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index cd0bc32..6d571d7 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -28,7 +28,7 @@
class ProximityInfo {
public:
- ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize,
+ ProximityInfo(JNIEnv *env, const jstring localeJStr,
const int keyboardWidth, const int keyboardHeight, const int gridWidth,
const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
@@ -126,7 +126,6 @@
float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
bool hasInputCoordinates() const;
- const int MAX_PROXIMITY_CHARS_SIZE;
const int GRID_WIDTH;
const int GRID_HEIGHT;
const int MOST_COMMON_KEY_WIDTH;
diff --git a/native/jni/src/proximity_info_params.cpp b/native/jni/src/proximity_info_params.cpp
new file mode 100644
index 0000000..5a51f62
--- /dev/null
+++ b/native/jni/src/proximity_info_params.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "proximity_info_params.h"
+
+namespace latinime {
+const int ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE = 50;
+const int ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
+const int ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
+const int ProximityInfoParams::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
+const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
+const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
+ 1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
+const float ProximityInfoParams::NOT_A_DISTANCE_FLOAT = -1.0f;
+// TODO: Investigate if this is required
+const float ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO = 0.95f;
+} // namespace latinime
diff --git a/native/jni/src/proximity_info_params.h b/native/jni/src/proximity_info_params.h
new file mode 100644
index 0000000..b941fec
--- /dev/null
+++ b/native/jni/src/proximity_info_params.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_PROXIMITY_INFO_PARAMS_H
+#define LATINIME_PROXIMITY_INFO_PARAMS_H
+
+#include "defines.h"
+
+namespace latinime {
+
+class ProximityInfoParams {
+ public:
+ static const int LOOKUP_RADIUS_PERCENTILE;
+ static const int FIRST_POINT_TIME_OFFSET_MILLIS;
+ static const int STRONG_DOUBLE_LETTER_TIME_MILLIS;
+ static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
+ static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
+ static const float NOT_A_DISTANCE_FLOAT;
+ static const float SEARCH_KEY_RADIUS_RATIO;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfoParams);
+ static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
+};
+} // namespace latinime
+#endif // LATINIME_PROXIMITY_INFO_PARAMS_H
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index 31b6e4b..8c6a525 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -27,15 +27,7 @@
namespace latinime {
-const int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
-const int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
- 1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
-const float ProximityInfoState::NOT_A_DISTANCE_FLOAT = -1.0f;
const int ProximityInfoState::NOT_A_CODE = -1;
-const int ProximityInfoState::LOOKUP_RADIUS_PERCENTILE = 50;
-const int ProximityInfoState::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
-const int ProximityInfoState::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
-const int ProximityInfoState::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
@@ -65,10 +57,10 @@
int pushTouchPointStartIndex = 0;
int lastSavedInputSize = 0;
mMaxPointToKeyLength = maxPointToKeyLength;
- if (mIsContinuationPossible && mInputIndice.size() > 1) {
+ if (mIsContinuationPossible && mSampledInputIndice.size() > 1) {
// Just update difference.
// Two points prior is never skipped. Thus, we pop 2 input point data here.
- pushTouchPointStartIndex = mInputIndice[mInputIndice.size() - 2];
+ pushTouchPointStartIndex = mSampledInputIndice[mSampledInputIndice.size() - 2];
popInputData();
popInputData();
lastSavedInputSize = mSampledInputXs.size();
@@ -76,12 +68,12 @@
// Clear all data.
mSampledInputXs.clear();
mSampledInputYs.clear();
- mTimes.clear();
- mInputIndice.clear();
- mLengthCache.clear();
- mDistanceCache_G.clear();
- mNearKeysVector.clear();
- mSearchKeysVector.clear();
+ mSampledTimes.clear();
+ mSampledInputIndice.clear();
+ mSampledLengthCache.clear();
+ mSampledDistanceCache_G.clear();
+ mSampledNearKeysVector.clear();
+ mSampledSearchKeysVector.clear();
mSpeedRates.clear();
mBeelineSpeedPercentiles.clear();
mCharProbabilities.clear();
@@ -97,92 +89,43 @@
mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(
mProximityInfo->getMostCommonKeyWidth(), mProximityInfo, mMaxPointToKeyLength,
mInputProximities, xCoordinates, yCoordinates, times, pointerIds, inputSize,
- isGeometric, pointerId, pushTouchPointStartIndex,
- &mSampledInputXs, &mSampledInputYs, &mTimes, &mLengthCache, &mInputIndice);
+ isGeometric, pointerId, pushTouchPointStartIndex, &mSampledInputXs,
+ &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice);
}
if (mSampledInputSize > 0 && isGeometric) {
- refreshSpeedRates(inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize);
- refreshBeelineSpeedRates(inputSize, xCoordinates, yCoordinates, times);
- }
-
- if (DEBUG_GEO_FULL) {
- for (int i = 0; i < mSampledInputSize; ++i) {
- AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mSampledInputXs[i],
- mSampledInputYs[i], mTimes[i]);
- }
+ mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(
+ inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize,
+ mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes,
+ &mSampledLengthCache, &mSampledInputIndice, &mSpeedRates, &mDirections);
+ ProximityInfoStateUtils::refreshBeelineSpeedRates(
+ mProximityInfo->getMostCommonKeyWidth(), mAverageSpeed, inputSize,
+ xCoordinates, yCoordinates, times, mSampledInputSize, &mSampledInputXs,
+ &mSampledInputYs, &mSampledInputIndice, &mBeelineSpeedPercentiles);
}
if (mSampledInputSize > 0) {
- const int keyCount = mProximityInfo->getKeyCount();
- mNearKeysVector.resize(mSampledInputSize);
- mSearchKeysVector.resize(mSampledInputSize);
- mDistanceCache_G.resize(mSampledInputSize * keyCount);
- for (int i = lastSavedInputSize; i < mSampledInputSize; ++i) {
- mNearKeysVector[i].reset();
- mSearchKeysVector[i].reset();
- static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
- for (int k = 0; k < keyCount; ++k) {
- const int index = i * keyCount + k;
- const int x = mSampledInputXs[i];
- const int y = mSampledInputYs[i];
- const float normalizedSquaredDistance =
- mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
- mDistanceCache_G[index] = normalizedSquaredDistance;
- if (normalizedSquaredDistance < NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
- mNearKeysVector[i][k] = true;
- }
- }
- }
+ ProximityInfoStateUtils::initGeometricDistanceInfos(
+ mProximityInfo, mProximityInfo->getKeyCount(),
+ mSampledInputSize, lastSavedInputSize, &mSampledInputXs, &mSampledInputYs,
+ &mSampledNearKeysVector, &mSampledDistanceCache_G);
if (isGeometric) {
// updates probabilities of skipping or mapping each key for all points.
- updateAlignPointProbabilities(lastSavedInputSize);
-
- static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
- const int readForwordLength = static_cast<int>(
- hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
- * READ_FORWORD_LENGTH_SCALE);
- for (int i = 0; i < mSampledInputSize; ++i) {
- if (i >= lastSavedInputSize) {
- mSearchKeysVector[i].reset();
- }
- for (int j = max(i, lastSavedInputSize); j < mSampledInputSize; ++j) {
- if (mLengthCache[j] - mLengthCache[i] >= readForwordLength) {
- break;
- }
- mSearchKeysVector[i] |= mNearKeysVector[j];
- }
- }
+ ProximityInfoStateUtils::updateAlignPointProbabilities(
+ mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(),
+ mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize,
+ &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache,
+ &mSampledDistanceCache_G, &mSampledNearKeysVector, &mCharProbabilities);
+ ProximityInfoStateUtils::updateSampledSearchKeysVector(mProximityInfo,
+ mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
+ &mSampledNearKeysVector, &mSampledSearchKeysVector);
}
}
if (DEBUG_SAMPLING_POINTS) {
- std::stringstream originalX, originalY, sampledX, sampledY;
- for (int i = 0; i < inputSize; ++i) {
- originalX << xCoordinates[i];
- originalY << yCoordinates[i];
- if (i != inputSize - 1) {
- originalX << ";";
- originalY << ";";
- }
- }
- AKLOGI("===== sampled points =====");
- for (int i = 0; i < mSampledInputSize; ++i) {
- if (isGeometric) {
- AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d",
- i, mSampledInputXs[i], mSampledInputYs[i], mTimes[i], mSpeedRates[i],
- getBeelineSpeedPercentile(i));
- }
- sampledX << mSampledInputXs[i];
- sampledY << mSampledInputYs[i];
- if (i != mSampledInputSize - 1) {
- sampledX << ";";
- sampledY << ";";
- }
- }
- AKLOGI("original points:\n%s, %s,\nsampled points:\n%s, %s,\n",
- originalX.str().c_str(), originalY.str().c_str(), sampledX.str().c_str(),
- sampledY.str().c_str());
+ ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates,
+ mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates,
+ &mBeelineSpeedPercentiles);
}
// end
///////////////////////
@@ -192,200 +135,28 @@
mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData
&& xCoordinates && yCoordinates;
if (!isGeometric && pointerId == 0) {
- for (int i = 0; i < inputSize; ++i) {
- mPrimaryInputWord[i] = getPrimaryCodePointAt(i);
- }
-
- for (int i = 0; i < mSampledInputSize && mTouchPositionCorrectionEnabled; ++i) {
- const int *proximityCodePoints = getProximityCodePointsAt(i);
- const int primaryKey = proximityCodePoints[0];
- const int x = xCoordinates[i];
- const int y = yCoordinates[i];
- if (DEBUG_PROXIMITY_CHARS) {
- int a = x + y + primaryKey;
- a += 0;
- AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y);
- }
- for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityCodePoints[j] > 0;
- ++j) {
- const int currentCodePoint = proximityCodePoints[j];
- const float squaredDistance =
- hasInputCoordinates() ? calculateNormalizedSquaredDistance(
- mProximityInfo->getKeyIndexOf(currentCodePoint), i) :
- NOT_A_DISTANCE_FLOAT;
- if (squaredDistance >= 0.0f) {
- mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
- (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
- } else {
- mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
- (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO :
- PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
- }
- if (DEBUG_PROXIMITY_CHARS) {
- AKLOGI("--- Proximity (%d) = %c", j, currentCodePoint);
- }
- }
+ ProximityInfoStateUtils::initPrimaryInputWord(
+ inputSize, mInputProximities, mPrimaryInputWord);
+ if (mTouchPositionCorrectionEnabled) {
+ ProximityInfoStateUtils::initNormalizedSquaredDistances(
+ mProximityInfo, inputSize, xCoordinates, yCoordinates, mInputProximities,
+ hasInputCoordinates(), &mSampledInputXs, &mSampledInputYs,
+ mNormalizedSquaredDistances);
}
}
-
if (DEBUG_GEO_FULL) {
AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
}
}
-void ProximityInfoState::refreshSpeedRates(const int inputSize, const int *const xCoordinates,
- const int *const yCoordinates, const int *const times, const int lastSavedInputSize) {
- // Relative speed calculation.
- const int sumDuration = mTimes.back() - mTimes.front();
- const int sumLength = mLengthCache.back() - mLengthCache.front();
- mAverageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
- mSpeedRates.resize(mSampledInputSize);
- for (int i = lastSavedInputSize; i < mSampledInputSize; ++i) {
- const int index = mInputIndice[i];
- int length = 0;
- int duration = 0;
-
- // Calculate velocity by using distances and durations of
- // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
- static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2;
- for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
- ++j) {
- if (i < mSampledInputSize - 1 && j >= mInputIndice[i + 1]) {
- break;
- }
- length += getDistanceInt(xCoordinates[j], yCoordinates[j],
- xCoordinates[j + 1], yCoordinates[j + 1]);
- duration += times[j + 1] - times[j];
- }
- for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
- if (i > 0 && j < mInputIndice[i - 1]) {
- break;
- }
- // TODO: use mLengthCache instead?
- length += getDistanceInt(xCoordinates[j], yCoordinates[j],
- xCoordinates[j + 1], yCoordinates[j + 1]);
- duration += times[j + 1] - times[j];
- }
- if (duration == 0 || sumDuration == 0) {
- // Cannot calculate speed; thus, it gives an average value (1.0);
- mSpeedRates[i] = 1.0f;
- } else {
- const float speed = static_cast<float>(length) / static_cast<float>(duration);
- mSpeedRates[i] = speed / mAverageSpeed;
- }
- }
-
- // Direction calculation.
- mDirections.resize(mSampledInputSize - 1);
- for (int i = max(0, lastSavedInputSize - 1); i < mSampledInputSize - 1; ++i) {
- mDirections[i] = getDirection(i, i + 1);
- }
-}
-
-static const int MAX_PERCENTILE = 100;
-void ProximityInfoState::refreshBeelineSpeedRates(const int inputSize,
- const int *const xCoordinates, const int *const yCoordinates, const int * times) {
- if (DEBUG_SAMPLING_POINTS){
- AKLOGI("--- refresh beeline speed rates");
- }
- mBeelineSpeedPercentiles.resize(mSampledInputSize);
- for (int i = 0; i < mSampledInputSize; ++i) {
- mBeelineSpeedPercentiles[i] = static_cast<int>(calculateBeelineSpeedRate(
- i, inputSize, xCoordinates, yCoordinates, times) * MAX_PERCENTILE);
- }
-}
-
-float ProximityInfoState::calculateBeelineSpeedRate(
- const int id, const int inputSize, const int *const xCoordinates,
- const int *const yCoordinates, const int * times) const {
- if (mSampledInputSize <= 0 || mAverageSpeed < 0.001f) {
- if (DEBUG_SAMPLING_POINTS){
- AKLOGI("--- invalid state: cancel. size = %d, ave = %f",
- mSampledInputSize, mAverageSpeed);
- }
- return 1.0f;
- }
- const int lookupRadius =
- mProximityInfo->getMostCommonKeyWidth() * LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE;
- const int x0 = mSampledInputXs[id];
- const int y0 = mSampledInputYs[id];
- const int actualInputIndex = mInputIndice[id];
- int tempTime = 0;
- int tempBeelineDistance = 0;
- int start = actualInputIndex;
- // lookup forward
- while (start > 0 && tempBeelineDistance < lookupRadius) {
- tempTime += times[start] - times[start - 1];
- --start;
- tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[start], yCoordinates[start]);
- }
- // Exclusive unless this is an edge point
- if (start > 0 && start < actualInputIndex) {
- ++start;
- }
- tempTime= 0;
- tempBeelineDistance = 0;
- int end = actualInputIndex;
- // lookup backward
- while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) {
- tempTime += times[end + 1] - times[end];
- ++end;
- tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[end], yCoordinates[end]);
- }
- // Exclusive unless this is an edge point
- if (end > actualInputIndex && end < (inputSize - 1)) {
- --end;
- }
-
- if (start >= end) {
- if (DEBUG_DOUBLE_LETTER) {
- AKLOGI("--- double letter: start == end %d", start);
- }
- return 1.0f;
- }
-
- const int x2 = xCoordinates[start];
- const int y2 = yCoordinates[start];
- const int x3 = xCoordinates[end];
- const int y3 = yCoordinates[end];
- const int beelineDistance = getDistanceInt(x2, y2, x3, y3);
- int adjustedStartTime = times[start];
- if (start == 0 && actualInputIndex == 0 && inputSize > 1) {
- adjustedStartTime += FIRST_POINT_TIME_OFFSET_MILLIS;
- }
- int adjustedEndTime = times[end];
- if (end == (inputSize - 1) && inputSize > 1) {
- adjustedEndTime -= FIRST_POINT_TIME_OFFSET_MILLIS;
- }
- const int time = adjustedEndTime - adjustedStartTime;
- if (time <= 0) {
- return 1.0f;
- }
-
- if (time >= STRONG_DOUBLE_LETTER_TIME_MILLIS){
- return 0.0f;
- }
- if (DEBUG_DOUBLE_LETTER) {
- AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d, speed = %f,"
- " ave = %f, val = %f, start time = %d, end time = %d",
- id, mInputIndice[id], start, end, beelineDistance, time,
- (static_cast<float>(beelineDistance) / static_cast<float>(time)), mAverageSpeed,
- ((static_cast<float>(beelineDistance) / static_cast<float>(time)) / mAverageSpeed),
- adjustedStartTime, adjustedEndTime);
- }
- // Offset 1%
- // TODO: Detect double letter more smartly
- return 0.01f + static_cast<float>(beelineDistance) / static_cast<float>(time) / mAverageSpeed;
-}
-
bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSize,
const int *const xCoordinates, const int *const yCoordinates, const int *const times,
const bool isGeometric) const {
if (isGeometric) {
for (int i = 0; i < mSampledInputSize; ++i) {
- const int index = mInputIndice[i];
+ const int index = mSampledInputIndice[i];
if (index > inputSize || xCoordinates[index] != mSampledInputXs[i] ||
- yCoordinates[index] != mSampledInputYs[i] || times[index] != mTimes[i]) {
+ yCoordinates[index] != mSampledInputYs[i] || times[index] != mSampledTimes[i]) {
return false;
}
}
@@ -404,26 +175,9 @@
return true;
}
-float ProximityInfoState::calculateNormalizedSquaredDistance(
- const int keyIndex, const int inputIndex) const {
- if (keyIndex == NOT_AN_INDEX) {
- return NOT_A_DISTANCE_FLOAT;
- }
- if (!mProximityInfo->hasSweetSpotData(keyIndex)) {
- return NOT_A_DISTANCE_FLOAT;
- }
- if (NOT_A_COORDINATE == mSampledInputXs[inputIndex]) {
- return NOT_A_DISTANCE_FLOAT;
- }
- const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(
- keyIndex, inputIndex);
- const float squaredRadius = square(mProximityInfo->getSweetSpotRadiiAt(keyIndex));
- return squaredDistance / squaredRadius;
-}
-
int ProximityInfoState::getDuration(const int index) const {
if (index >= 0 && index < mSampledInputSize - 1) {
- return mTimes[index + 1] - mTimes[index];
+ return mSampledTimes[index + 1] - mSampledTimes[index];
}
return 0;
}
@@ -436,7 +190,7 @@
const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
if (keyId != NOT_AN_INDEX) {
const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
- return min(mDistanceCache_G[index] * scale, mMaxPointToKeyLength);
+ return min(mSampledDistanceCache_G[index] * scale, mMaxPointToKeyLength);
}
if (isSkippableCodePoint(codePoint)) {
return 0.0f;
@@ -450,16 +204,10 @@
}
// TODO: Remove the "scale" parameter
-// This function basically converts from a length to an edit distance. Accordingly, it's obviously
-// wrong to compare with mMaxPointToKeyLength.
float ProximityInfoState::getPointToKeyByIdLength(
const int inputIndex, const int keyId, const float scale) const {
- if (keyId != NOT_AN_INDEX) {
- const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
- return min(mDistanceCache_G[index] * scale, mMaxPointToKeyLength);
- }
- // If the char is not a key on the keyboard then return the max length.
- return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+ return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength,
+ &mSampledDistanceCache_G, mProximityInfo->getKeyCount(), inputIndex, keyId, scale);
}
float ProximityInfoState::getPointToKeyByIdLength(const int inputIndex, const int keyId) const {
@@ -498,7 +246,7 @@
// Not an exact nor an accent-alike match: search the list of close keys
int j = 1;
- while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ while (j < MAX_PROXIMITY_CHARS_SIZE
&& currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
const bool matched = (currentCodePoints[j] == baseLowerC || currentCodePoints[j] == c);
if (matched) {
@@ -509,10 +257,10 @@
}
++j;
}
- if (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ if (j < MAX_PROXIMITY_CHARS_SIZE
&& currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
++j;
- while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ while (j < MAX_PROXIMITY_CHARS_SIZE
&& currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
const bool matched = (currentCodePoints[j] == baseLowerC || currentCodePoints[j] == c);
if (matched) {
@@ -533,15 +281,6 @@
return mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
}
-float ProximityInfoState::calculateSquaredDistanceFromSweetSpotCenter(
- const int keyIndex, const int inputIndex) const {
- const float sweetSpotCenterX = mProximityInfo->getSweetSpotCenterXAt(keyIndex);
- const float sweetSpotCenterY = mProximityInfo->getSweetSpotCenterYAt(keyIndex);
- const float inputX = static_cast<float>(mSampledInputXs[inputIndex]);
- const float inputY = static_cast<float>(mSampledInputYs[inputIndex]);
- return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY);
-}
-
// Puts possible characters into filter and returns new filter size.
int ProximityInfoState::getAllPossibleChars(
const size_t index, int *const filter, const int filterSize) const {
@@ -551,7 +290,7 @@
int newFilterSize = filterSize;
const int keyCount = mProximityInfo->getKeyCount();
for (int j = 0; j < keyCount; ++j) {
- if (mSearchKeysVector[index].test(j)) {
+ if (mSampledSearchKeysVector[index].test(j)) {
const int keyCodePoint = mProximityInfo->getCodePointOf(j);
bool insert = true;
// TODO: Avoid linear search
@@ -572,52 +311,17 @@
bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
ASSERT(keyId >= 0);
ASSERT(index >= 0 && index < mSampledInputSize);
- return mSearchKeysVector[index].test(keyId);
+ return mSampledSearchKeysVector[index].test(keyId);
}
void ProximityInfoState::popInputData() {
- ProximityInfoStateUtils::popInputData(&mSampledInputXs, &mSampledInputYs, &mTimes,
- &mLengthCache, &mInputIndice);
+ ProximityInfoStateUtils::popInputData(&mSampledInputXs, &mSampledInputYs, &mSampledTimes,
+ &mSampledLengthCache, &mSampledInputIndice);
}
float ProximityInfoState::getDirection(const int index0, const int index1) const {
- if (index0 < 0 || index0 > mSampledInputSize - 1) {
- return 0.0f;
- }
- if (index1 < 0 || index1 > mSampledInputSize - 1) {
- return 0.0f;
- }
- const int x1 = mSampledInputXs[index0];
- const int y1 = mSampledInputYs[index0];
- const int x2 = mSampledInputXs[index1];
- const int y2 = mSampledInputYs[index1];
- return getAngle(x1, y1, x2, y2);
-}
-
-float ProximityInfoState::getPointAngle(const int index) const {
- if (index <= 0 || index >= mSampledInputSize - 1) {
- return 0.0f;
- }
- const float previousDirection = getDirection(index - 1, index);
- const float nextDirection = getDirection(index, index + 1);
- const float directionDiff = getAngleDiff(previousDirection, nextDirection);
- return directionDiff;
-}
-
-float ProximityInfoState::getPointsAngle(
- const int index0, const int index1, const int index2) const {
- if (index0 < 0 || index0 > mSampledInputSize - 1) {
- return 0.0f;
- }
- if (index1 < 0 || index1 > mSampledInputSize - 1) {
- return 0.0f;
- }
- if (index2 < 0 || index2 > mSampledInputSize - 1) {
- return 0.0f;
- }
- const float previousDirection = getDirection(index0, index1);
- const float nextDirection = getDirection(index1, index2);
- return getAngleDiff(previousDirection, nextDirection);
+ return ProximityInfoStateUtils::getDirection(
+ &mSampledInputXs, &mSampledInputYs, index0, index1);
}
float ProximityInfoState::getLineToKeyDistance(
@@ -640,293 +344,6 @@
keyX, keyY, x0, y0, x1, y1, extend);
}
-// Updates probabilities of aligning to some keys and skipping.
-// Word suggestion should be based on this probabilities.
-void ProximityInfoState::updateAlignPointProbabilities(const int start) {
- static const float MIN_PROBABILITY = 0.000001f;
- static const float MAX_SKIP_PROBABILITY = 0.95f;
- static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
- static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
- static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
- static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
- static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
- static const float NEAREST_DISTANCE_WEIGHT = 0.5f;
- static const float NEAREST_DISTANCE_BIAS = 0.5f;
- static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
- static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
-
- static const float ANGLE_WEIGHT = 0.90f;
- static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
- static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
- static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
- static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
- static const float SKIP_CORNER_PROBABILITY = 0.4f;
- static const float SPEED_MARGIN = 0.1f;
- static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
-
- const int keyCount = mProximityInfo->getKeyCount();
- mCharProbabilities.resize(mSampledInputSize);
- // Calculates probabilities of using a point as a correlated point with the character
- // for each point.
- for (int i = start; i < mSampledInputSize; ++i) {
- mCharProbabilities[i].clear();
- // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY.
- // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
- float skipProbability = MAX_SKIP_PROBABILITY;
-
- const float currentAngle = getPointAngle(i);
- const float speedRate = getSpeedRate(i);
-
- float nearestKeyDistance = static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
- for (int j = 0; j < keyCount; ++j) {
- if (mNearKeysVector[i].test(j)) {
- const float distance = getPointToKeyByIdLength(i, j);
- if (distance < nearestKeyDistance) {
- nearestKeyDistance = distance;
- }
- }
- }
-
- if (i == 0) {
- skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT
- + NEAREST_DISTANCE_BIAS);
- // Promote the first point
- skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
- } else if (i == mSampledInputSize - 1) {
- skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST
- + NEAREST_DISTANCE_BIAS_FOR_LAST);
- // Promote the last point
- skipProbability *= SKIP_LAST_POINT_PROBABILITY;
- } else {
- // If the current speed is relatively slower than adjacent keys, we promote this point.
- if (getSpeedRate(i - 1) - SPEED_MARGIN > speedRate
- && speedRate < getSpeedRate(i + 1) - SPEED_MARGIN) {
- if (currentAngle < CORNER_ANGLE_THRESHOLD) {
- skipProbability *= min(1.0f, speedRate
- * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
- } else {
- // If the angle is small enough, we promote this point more. (e.g. pit vs put)
- skipProbability *= min(1.0f, speedRate * SPEED_WEIGHT_FOR_SKIP_PROBABILITY
- + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
- }
- }
-
- skipProbability *= min(1.0f, speedRate * nearestKeyDistance *
- NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS);
-
- // Adjusts skip probability by a rate depending on angle.
- // ANGLE_RATE of skipProbability is adjusted by current angle.
- skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT
- + (1.0f - ANGLE_WEIGHT);
- if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
- skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
- }
- // We assume the angle of this point is the angle for point[i], point[i - 2]
- // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
- // and point[i - 2] is this angle can be more affected by the noise.
- const float prevAngle = getPointsAngle(i, i - 2, i - 3);
- if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD
- && currentAngle > CORNER_ANGLE_THRESHOLD) {
- skipProbability *= SKIP_CORNER_PROBABILITY;
- }
- }
-
- // probabilities must be in [0.0, MAX_SKIP_PROBABILITY];
- ASSERT(skipProbability >= 0.0f);
- ASSERT(skipProbability <= MAX_SKIP_PROBABILITY);
- mCharProbabilities[i][NOT_AN_INDEX] = skipProbability;
-
- // Second, calculates key probabilities by dividing the rest probability
- // (1.0f - skipProbability).
- const float inputCharProbability = 1.0f - skipProbability;
-
- // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
- // learning or something would be efficient.
- static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
- static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
- static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
- static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
- static const float MIN_STANDERD_DIVIATION = 0.37f;
-
- const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F
- * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
- MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
- const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance
- * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
- MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
- const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION;
-
- ProximityInfoUtils::NormalDistribution
- distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
- static const float PREV_DISTANCE_WEIGHT = 0.5f;
- static const float NEXT_DISTANCE_WEIGHT = 0.6f;
- // Summing up probability densities of all near keys.
- float sumOfProbabilityDensities = 0.0f;
- for (int j = 0; j < keyCount; ++j) {
- if (mNearKeysVector[i].test(j)) {
- float distance = sqrtf(getPointToKeyByIdLength(i, j));
- if (i == 0 && i != mSampledInputSize - 1) {
- // For the first point, weighted average of distances from first point and the
- // next point to the key is used as a point to key distance.
- const float nextDistance = sqrtf(getPointToKeyByIdLength(i + 1, j));
- if (nextDistance < distance) {
- // The distance of the first point tends to bigger than continuing
- // points because the first touch by the user can be sloppy.
- // So we promote the first point if the distance of that point is larger
- // than the distance of the next point.
- distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT)
- / (1.0f + NEXT_DISTANCE_WEIGHT);
- }
- } else if (i != 0 && i == mSampledInputSize - 1) {
- // For the first point, weighted average of distances from last point and
- // the previous point to the key is used as a point to key distance.
- const float previousDistance = sqrtf(getPointToKeyByIdLength(i - 1, j));
- if (previousDistance < distance) {
- // The distance of the last point tends to bigger than continuing points
- // because the last touch by the user can be sloppy. So we promote the
- // last point if the distance of that point is larger than the distance of
- // the previous point.
- distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT)
- / (1.0f + PREV_DISTANCE_WEIGHT);
- }
- }
- // TODO: Promote the first point when the extended line from the next input is near
- // from a key. Also, promote the last point as well.
- sumOfProbabilityDensities += distribution.getProbabilityDensity(distance);
- }
- }
-
- // Split the probability of an input point to keys that are close to the input point.
- for (int j = 0; j < keyCount; ++j) {
- if (mNearKeysVector[i].test(j)) {
- float distance = sqrtf(getPointToKeyByIdLength(i, j));
- if (i == 0 && i != mSampledInputSize - 1) {
- // For the first point, weighted average of distances from the first point and
- // the next point to the key is used as a point to key distance.
- const float prevDistance = sqrtf(getPointToKeyByIdLength(i + 1, j));
- if (prevDistance < distance) {
- distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT)
- / (1.0f + NEXT_DISTANCE_WEIGHT);
- }
- } else if (i != 0 && i == mSampledInputSize - 1) {
- // For the first point, weighted average of distances from last point and
- // the previous point to the key is used as a point to key distance.
- const float prevDistance = sqrtf(getPointToKeyByIdLength(i - 1, j));
- if (prevDistance < distance) {
- distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT)
- / (1.0f + PREV_DISTANCE_WEIGHT);
- }
- }
- const float probabilityDensity = distribution.getProbabilityDensity(distance);
- const float probability = inputCharProbability * probabilityDensity
- / sumOfProbabilityDensities;
- mCharProbabilities[i][j] = probability;
- }
- }
- }
-
-
- if (DEBUG_POINTS_PROBABILITY) {
- for (int i = 0; i < mSampledInputSize; ++i) {
- std::stringstream sstream;
- sstream << i << ", ";
- sstream << "(" << mSampledInputXs[i] << ", " << mSampledInputYs[i] << "), ";
- sstream << "Speed: "<< getSpeedRate(i) << ", ";
- sstream << "Angle: "<< getPointAngle(i) << ", \n";
-
- for (hash_map_compat<int, float>::iterator it = mCharProbabilities[i].begin();
- it != mCharProbabilities[i].end(); ++it) {
- if (it->first == NOT_AN_INDEX) {
- sstream << it->first
- << "(skip):"
- << it->second
- << "\n";
- } else {
- sstream << it->first
- << "("
- << static_cast<char>(mProximityInfo->getCodePointOf(it->first))
- << "):"
- << it->second
- << "\n";
- }
- }
- AKLOGI("%s", sstream.str().c_str());
- }
- }
-
- // Decrease key probabilities of points which don't have the highest probability of that key
- // among nearby points. Probabilities of the first point and the last point are not suppressed.
- for (int i = max(start, 1); i < mSampledInputSize; ++i) {
- for (int j = i + 1; j < mSampledInputSize; ++j) {
- if (!suppressCharProbabilities(i, j)) {
- break;
- }
- }
- for (int j = i - 1; j >= max(start, 0); --j) {
- if (!suppressCharProbabilities(i, j)) {
- break;
- }
- }
- }
-
- // Converting from raw probabilities to log probabilities to calculate spatial distance.
- for (int i = start; i < mSampledInputSize; ++i) {
- for (int j = 0; j < keyCount; ++j) {
- hash_map_compat<int, float>::iterator it = mCharProbabilities[i].find(j);
- if (it == mCharProbabilities[i].end()){
- mNearKeysVector[i].reset(j);
- } else if(it->second < MIN_PROBABILITY) {
- // Erases from near keys vector because it has very low probability.
- mNearKeysVector[i].reset(j);
- mCharProbabilities[i].erase(j);
- } else {
- it->second = -logf(it->second);
- }
- }
- mCharProbabilities[i][NOT_AN_INDEX] = -logf(mCharProbabilities[i][NOT_AN_INDEX]);
- }
-}
-
-// Decreases char probabilities of index0 by checking probabilities of a near point (index1) and
-// increases char probabilities of index1 by checking probabilities of index0.
-bool ProximityInfoState::suppressCharProbabilities(const int index0, const int index1) {
- ASSERT(0 <= index0 && index0 < mSampledInputSize);
- ASSERT(0 <= index1 && index1 < mSampledInputSize);
-
- static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
- static const float MIN_SUPPRESSION_RATE = 0.1f;
- static const float SUPPRESSION_WEIGHT = 0.5f;
- static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
- static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
-
- const float keyWidthFloat = static_cast<float>(mProximityInfo->getMostCommonKeyWidth());
- const float diff = fabsf(static_cast<float>(mLengthCache[index0] - mLengthCache[index1]));
- if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
- return false;
- }
- const float suppressionRate = MIN_SUPPRESSION_RATE
- + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT;
- for (hash_map_compat<int, float>::iterator it = mCharProbabilities[index0].begin();
- it != mCharProbabilities[index0].end(); ++it) {
- hash_map_compat<int, float>::iterator it2 = mCharProbabilities[index1].find(it->first);
- if (it2 != mCharProbabilities[index1].end() && it->second < it2->second) {
- const float newProbability = it->second * suppressionRate;
- const float suppression = it->second - newProbability;
- it->second = newProbability;
- // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point.
- mCharProbabilities[index0][NOT_AN_INDEX] += suppression;
-
- // Add the probability of the same key nearby index1
- const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
- mCharProbabilities[index1][NOT_AN_INDEX]
- * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
- it2->second += probabilityGain;
- mCharProbabilities[index1][NOT_AN_INDEX] -= probabilityGain;
- }
- }
- return true;
-}
-
// Get a word that is detected by tracing the most probable string into codePointBuf and
// returns probability of generating the word.
float ProximityInfoState::getMostProbableString(int *const codePointBuf) const {
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
index 0f0eb7d..642925c 100644
--- a/native/jni/src/proximity_info_state.h
+++ b/native/jni/src/proximity_info_state.h
@@ -17,13 +17,13 @@
#ifndef LATINIME_PROXIMITY_INFO_STATE_H
#define LATINIME_PROXIMITY_INFO_STATE_H
-#include <bitset>
#include <cstring> // for memset()
#include <vector>
#include "char_utils.h"
#include "defines.h"
#include "hash_map_compat.h"
+#include "proximity_info_params.h"
#include "proximity_info_state_utils.h"
namespace latinime {
@@ -32,15 +32,8 @@
class ProximityInfoState {
public:
- typedef std::bitset<MAX_KEY_COUNT_IN_A_KEYBOARD> NearKeycodesSet;
- static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
- static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
- static const float NOT_A_DISTANCE_FLOAT;
+
static const int NOT_A_CODE;
- static const int LOOKUP_RADIUS_PERCENTILE;
- static const int FIRST_POINT_TIME_OFFSET_MILLIS;
- static const int STRONG_DOUBLE_LETTER_TIME_MILLIS;
- static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
/////////////////////////////////////////
// Defined in proximity_info_state.cpp //
@@ -57,10 +50,11 @@
: mProximityInfo(0), mMaxPointToKeyLength(0.0f), mAverageSpeed(0.0f),
mHasTouchPositionCorrectionData(false), mMostCommonKeyWidthSquare(0),
mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0),
- mIsContinuationPossible(false), mSampledInputXs(), mSampledInputYs(), mTimes(),
- mInputIndice(), mLengthCache(), mBeelineSpeedPercentiles(), mDistanceCache_G(),
- mSpeedRates(), mDirections(), mCharProbabilities(), mNearKeysVector(),
- mSearchKeysVector(), mTouchPositionCorrectionEnabled(false), mSampledInputSize(0) {
+ mIsContinuationPossible(false), mSampledInputXs(), mSampledInputYs(), mSampledTimes(),
+ mSampledInputIndice(), mSampledLengthCache(), mBeelineSpeedPercentiles(),
+ mSampledDistanceCache_G(), mSpeedRates(), mDirections(), mCharProbabilities(),
+ mSampledNearKeysVector(), mSampledSearchKeysVector(),
+ mTouchPositionCorrectionEnabled(false), mSampledInputSize(0) {
memset(mInputProximities, 0, sizeof(mInputProximities));
memset(mNormalizedSquaredDistances, 0, sizeof(mNormalizedSquaredDistances));
memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord));
@@ -76,7 +70,7 @@
AK_FORCE_INLINE bool existsCodePointInProximityAt(const int index, const int c) const {
const int *codePoints = getProximityCodePointsAt(index);
int i = 0;
- while (codePoints[i] > 0 && i < MAX_PROXIMITY_CHARS_SIZE_INTERNAL) {
+ while (codePoints[i] > 0 && i < MAX_PROXIMITY_CHARS_SIZE) {
if (codePoints[i++] == c) {
return true;
}
@@ -102,7 +96,7 @@
inline int getNormalizedSquaredDistance(
const int inputIndex, const int proximityIndex) const {
return mNormalizedSquaredDistances[
- inputIndex * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + proximityIndex];
+ inputIndex * MAX_PROXIMITY_CHARS_SIZE + proximityIndex];
}
inline const int *getPrimaryInputWord() const {
@@ -122,7 +116,7 @@
if (*inputProximities != *word) {
return false;
}
- inputProximities += MAX_PROXIMITY_CHARS_SIZE_INTERNAL;
+ inputProximities += MAX_PROXIMITY_CHARS_SIZE;
word++;
}
return true;
@@ -149,7 +143,7 @@
bool hasSpaceProximity(const int index) const;
int getLengthCache(const int index) const {
- return mLengthCache[index];
+ return mSampledLengthCache[index];
}
bool isContinuationPossible() const {
@@ -180,7 +174,8 @@
const int beelineSpeedRate = getBeelineSpeedPercentile(id);
if (beelineSpeedRate == 0) {
return A_STRONG_DOUBLE_LETTER;
- } else if (beelineSpeedRate < MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE) {
+ } else if (beelineSpeedRate
+ < ProximityInfoParams::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE) {
return A_DOUBLE_LETTER;
} else {
return NOT_A_DOUBLE_LETTER;
@@ -193,10 +188,6 @@
// get xy direction
float getDirection(const int x, const int y) const;
- float getPointAngle(const int index) const;
- // Returns angle of three points. x, y, and z are indices.
- float getPointsAngle(const int index0, const int index1, const int index2) const;
-
float getMostProbableString(int *const codePointBuf) const;
float getProbability(const int index, const int charCode) const;
@@ -207,7 +198,6 @@
bool isKeyInSerchKeysAfterIndex(const int index, const int keyId) const;
private:
DISALLOW_COPY_AND_ASSIGN(ProximityInfoState);
- typedef hash_map_compat<int, float> NearKeysDistanceMap;
/////////////////////////////////////////
// Defined in proximity_info_state.cpp //
/////////////////////////////////////////
@@ -216,15 +206,9 @@
float calculateSquaredDistanceFromSweetSpotCenter(
const int keyIndex, const int inputIndex) const;
- bool pushTouchPoint(const int inputIndex, const int nodeCodePoint, int x, int y, const int time,
- const bool sample, const bool isLastPoint, const float sumAngle,
- NearKeysDistanceMap *const currentNearKeysDistances,
- const NearKeysDistanceMap *const prevNearKeysDistances,
- const NearKeysDistanceMap *const prevPrevNearKeysDistances);
/////////////////////////////////////////
// Defined here //
/////////////////////////////////////////
- inline float square(const float x) const { return x * x; }
bool hasInputCoordinates() const {
return mSampledInputXs.size() > 0 && mSampledInputYs.size() > 0;
@@ -233,28 +217,9 @@
inline const int *getProximityCodePointsAt(const int index) const {
return ProximityInfoStateUtils::getProximityCodePointsAt(mInputProximities, index);
}
-
- float updateNearKeysDistances(const int x, const int y,
- NearKeysDistanceMap *const currentNearKeysDistances);
- bool isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances,
- const NearKeysDistanceMap *const prevNearKeysDistances,
- const NearKeysDistanceMap *const prevPrevNearKeysDistances) const;
- float getPointScore(
- const int x, const int y, const int time, const bool last, const float nearest,
- const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
- const NearKeysDistanceMap *const prevNearKeysDistances,
- const NearKeysDistanceMap *const prevPrevNearKeysDistances) const;
bool checkAndReturnIsContinuationPossible(const int inputSize, const int *const xCoordinates,
const int *const yCoordinates, const int *const times, const bool isGeometric) const;
void popInputData();
- void updateAlignPointProbabilities(const int start);
- bool suppressCharProbabilities(const int index1, const int index2);
- void refreshSpeedRates(const int inputSize, const int *const xCoordinates,
- const int *const yCoordinates, const int *const times, const int lastSavedInputSize);
- void refreshBeelineSpeedRates(const int inputSize,
- const int *const xCoordinates, const int *const yCoordinates, const int * times);
- float calculateBeelineSpeedRate(const int id, const int inputSize,
- const int *const xCoordinates, const int *const yCoordinates, const int * times) const;
// const
const ProximityInfo *mProximityInfo;
@@ -271,27 +236,27 @@
std::vector<int> mSampledInputXs;
std::vector<int> mSampledInputYs;
- std::vector<int> mTimes;
- std::vector<int> mInputIndice;
- std::vector<int> mLengthCache;
+ std::vector<int> mSampledTimes;
+ std::vector<int> mSampledInputIndice;
+ std::vector<int> mSampledLengthCache;
std::vector<int> mBeelineSpeedPercentiles;
- std::vector<float> mDistanceCache_G;
+ std::vector<float> mSampledDistanceCache_G;
std::vector<float> mSpeedRates;
std::vector<float> mDirections;
// probabilities of skipping or mapping to a key for each point.
std::vector<hash_map_compat<int, float> > mCharProbabilities;
// The vector for the key code set which holds nearby keys for each sampled input point
// 1. Used to calculate the probability of the key
- // 2. Used to calculate mSearchKeysVector
- std::vector<NearKeycodesSet> mNearKeysVector;
+ // 2. Used to calculate mSampledSearchKeysVector
+ std::vector<ProximityInfoStateUtils::NearKeycodesSet> mSampledNearKeysVector;
// The vector for the key code set which holds nearby keys of some trailing sampled input points
// for each sampled input point. These nearby keys contain the next characters which can be in
// the dictionary. Specifically, currently we are looking for keys nearby trailing sampled
// inputs including the current input point.
- std::vector<NearKeycodesSet> mSearchKeysVector;
+ std::vector<ProximityInfoStateUtils::NearKeycodesSet> mSampledSearchKeysVector;
bool mTouchPositionCorrectionEnabled;
- int mInputProximities[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH];
- int mNormalizedSquaredDistances[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH];
+ int mInputProximities[MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH];
+ int mNormalizedSquaredDistances[MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH];
int mSampledInputSize;
int mPrimaryInputWord[MAX_WORD_LENGTH];
};
diff --git a/native/jni/src/proximity_info_state_utils.cpp b/native/jni/src/proximity_info_state_utils.cpp
new file mode 100644
index 0000000..cbc191e
--- /dev/null
+++ b/native/jni/src/proximity_info_state_utils.cpp
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <cmath>
+#include <sstream> // for debug prints
+#include <vector>
+
+#include "defines.h"
+#include "geometry_utils.h"
+#include "proximity_info.h"
+#include "proximity_info_params.h"
+#include "proximity_info_state_utils.h"
+
+namespace latinime {
+
+/* static */ int ProximityInfoStateUtils::updateTouchPoints(const int mostCommonKeyWidth,
+ const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
+ const int *const inputProximities, const int *const inputXCoordinates,
+ const int *const inputYCoordinates, const int *const times, const int *const pointerIds,
+ const int inputSize, const bool isGeometric, const int pointerId,
+ const int pushTouchPointStartIndex, std::vector<int> *sampledInputXs,
+ std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
+ std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) {
+ if (DEBUG_SAMPLING_POINTS) {
+ if (times) {
+ for (int i = 0; i < inputSize; ++i) {
+ AKLOGI("(%d) x %d, y %d, time %d",
+ i, inputXCoordinates[i], inputYCoordinates[i], times[i]);
+ }
+ }
+ }
+#ifdef DO_ASSERT_TEST
+ if (times) {
+ for (int i = 0; i < inputSize; ++i) {
+ if (i > 0) {
+ if (times[i] < times[i - 1]) {
+ AKLOGI("Invalid time sequence. %d, %d", times[i - 1], times[i]);
+ ASSERT(false);
+ }
+ }
+ }
+ }
+#endif
+ const bool proximityOnly = !isGeometric
+ && (inputXCoordinates[0] < 0 || inputYCoordinates[0] < 0);
+ int lastInputIndex = pushTouchPointStartIndex;
+ for (int i = lastInputIndex; i < inputSize; ++i) {
+ const int pid = pointerIds ? pointerIds[i] : 0;
+ if (pointerId == pid) {
+ lastInputIndex = i;
+ }
+ }
+ if (DEBUG_GEO_FULL) {
+ AKLOGI("Init ProximityInfoState: last input index = %d", lastInputIndex);
+ }
+ // Working space to save near keys distances for current, prev and prevprev input point.
+ NearKeysDistanceMap nearKeysDistances[3];
+ // These pointers are swapped for each inputs points.
+ NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0];
+ NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1];
+ NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2];
+ // "sumAngle" is accumulated by each angle of input points. And when "sumAngle" exceeds
+ // the threshold we save that point, reset sumAngle. This aims to keep the figure of
+ // the curve.
+ float sumAngle = 0.0f;
+
+ for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) {
+ // Assuming pointerId == 0 if pointerIds is null.
+ const int pid = pointerIds ? pointerIds[i] : 0;
+ if (DEBUG_GEO_FULL) {
+ AKLOGI("Init ProximityInfoState: (%d)PID = %d", i, pid);
+ }
+ if (pointerId == pid) {
+ const int c = isGeometric ?
+ NOT_A_COORDINATE : getPrimaryCodePointAt(inputProximities, i);
+ const int x = proximityOnly ? NOT_A_COORDINATE : inputXCoordinates[i];
+ const int y = proximityOnly ? NOT_A_COORDINATE : inputYCoordinates[i];
+ const int time = times ? times[i] : -1;
+
+ if (i > 1) {
+ const float prevAngle = getAngle(
+ inputXCoordinates[i - 2], inputYCoordinates[i - 2],
+ inputXCoordinates[i - 1], inputYCoordinates[i - 1]);
+ const float currentAngle =
+ getAngle(inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
+ sumAngle += getAngleDiff(prevAngle, currentAngle);
+ }
+
+ if (pushTouchPoint(mostCommonKeyWidth, proximityInfo, maxPointToKeyLength,
+ i, c, x, y, time, isGeometric /* doSampling */,
+ i == lastInputIndex, sumAngle, currentNearKeysDistances,
+ prevNearKeysDistances, prevPrevNearKeysDistances,
+ sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
+ sampledInputIndice)) {
+ // Previous point information was popped.
+ NearKeysDistanceMap *tmp = prevNearKeysDistances;
+ prevNearKeysDistances = currentNearKeysDistances;
+ currentNearKeysDistances = tmp;
+ } else {
+ NearKeysDistanceMap *tmp = prevPrevNearKeysDistances;
+ prevPrevNearKeysDistances = prevNearKeysDistances;
+ prevNearKeysDistances = currentNearKeysDistances;
+ currentNearKeysDistances = tmp;
+ sumAngle = 0.0f;
+ }
+ }
+ }
+ return sampledInputXs->size();
+}
+
+/* static */ const int *ProximityInfoStateUtils::getProximityCodePointsAt(
+ const int *const inputProximities, const int index) {
+ return inputProximities + (index * MAX_PROXIMITY_CHARS_SIZE);
+}
+
+/* static */ int ProximityInfoStateUtils::getPrimaryCodePointAt(
+ const int *const inputProximities, const int index) {
+ return getProximityCodePointsAt(inputProximities, index)[0];
+}
+
+/* static */ void ProximityInfoStateUtils::initPrimaryInputWord(
+ const int inputSize, const int *const inputProximities, int *primaryInputWord) {
+ for (int i = 0; i < inputSize; ++i) {
+ primaryInputWord[i] = getPrimaryCodePointAt(inputProximities, i);
+ }
+}
+
+/* static */ float ProximityInfoStateUtils::calculateSquaredDistanceFromSweetSpotCenter(
+ const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int keyIndex,
+ const int inputIndex) {
+ const float sweetSpotCenterX = proximityInfo->getSweetSpotCenterXAt(keyIndex);
+ const float sweetSpotCenterY = proximityInfo->getSweetSpotCenterYAt(keyIndex);
+ const float inputX = static_cast<float>((*sampledInputXs)[inputIndex]);
+ const float inputY = static_cast<float>((*sampledInputYs)[inputIndex]);
+ return SQUARE_FLOAT(inputX - sweetSpotCenterX) + SQUARE_FLOAT(inputY - sweetSpotCenterY);
+}
+
+/* static */ float ProximityInfoStateUtils::calculateNormalizedSquaredDistance(
+ const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const int keyIndex, const int inputIndex) {
+ if (keyIndex == NOT_AN_INDEX) {
+ return ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
+ }
+ if (!proximityInfo->hasSweetSpotData(keyIndex)) {
+ return ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
+ }
+ if (NOT_A_COORDINATE == (*sampledInputXs)[inputIndex]) {
+ return ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
+ }
+ const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(proximityInfo,
+ sampledInputXs, sampledInputYs, keyIndex, inputIndex);
+ const float squaredRadius = SQUARE_FLOAT(proximityInfo->getSweetSpotRadiiAt(keyIndex));
+ return squaredDistance / squaredRadius;
+}
+
+/* static */ void ProximityInfoStateUtils::initNormalizedSquaredDistances(
+ const ProximityInfo *const proximityInfo, const int inputSize,
+ const int *inputXCoordinates, const int *inputYCoordinates,
+ const int *const inputProximities, const bool hasInputCoordinates,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ int *normalizedSquaredDistances) {
+ for (int i = 0; i < inputSize; ++i) {
+ const int *proximityCodePoints = getProximityCodePointsAt(inputProximities, i);
+ const int primaryKey = proximityCodePoints[0];
+ const int x = inputXCoordinates[i];
+ const int y = inputYCoordinates[i];
+ if (DEBUG_PROXIMITY_CHARS) {
+ int a = x + y + primaryKey;
+ a += 0;
+ AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y);
+ }
+ for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE && proximityCodePoints[j] > 0;
+ ++j) {
+ const int currentCodePoint = proximityCodePoints[j];
+ const float squaredDistance =
+ hasInputCoordinates ? calculateNormalizedSquaredDistance(
+ proximityInfo, sampledInputXs, sampledInputYs,
+ proximityInfo->getKeyIndexOf(currentCodePoint), i) :
+ ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
+ if (squaredDistance >= 0.0f) {
+ normalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] =
+ (int) (squaredDistance
+ * ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
+ } else {
+ normalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] =
+ (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO :
+ PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
+ }
+ if (DEBUG_PROXIMITY_CHARS) {
+ AKLOGI("--- Proximity (%d) = %c", j, currentCodePoint);
+ }
+ }
+ }
+
+}
+
+/* static */ void ProximityInfoStateUtils::initGeometricDistanceInfos(
+ const ProximityInfo *const proximityInfo, const int keyCount,
+ const int sampledInputSize, const int lastSavedInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ std::vector<NearKeycodesSet> *SampledNearKeysVector,
+ std::vector<float> *SampledDistanceCache_G) {
+ SampledNearKeysVector->resize(sampledInputSize);
+ SampledDistanceCache_G->resize(sampledInputSize * keyCount);
+ for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
+ (*SampledNearKeysVector)[i].reset();
+ static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
+ for (int k = 0; k < keyCount; ++k) {
+ const int index = i * keyCount + k;
+ const int x = (*sampledInputXs)[i];
+ const int y = (*sampledInputYs)[i];
+ const float normalizedSquaredDistance =
+ proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
+ (*SampledDistanceCache_G)[index] = normalizedSquaredDistance;
+ if (normalizedSquaredDistance < NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
+ (*SampledNearKeysVector)[i][k] = true;
+ }
+ }
+ }
+}
+
+/* static */ void ProximityInfoStateUtils::popInputData(std::vector<int> *sampledInputXs,
+ std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
+ std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) {
+ sampledInputXs->pop_back();
+ sampledInputYs->pop_back();
+ sampledInputTimes->pop_back();
+ sampledLengthCache->pop_back();
+ sampledInputIndice->pop_back();
+}
+
+/* static */ float ProximityInfoStateUtils::refreshSpeedRates(const int inputSize,
+ const int *const xCoordinates, const int *const yCoordinates, const int *const times,
+ const int lastSavedInputSize, const int sampledInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<int> *const sampledInputTimes,
+ const std::vector<int> *const sampledLengthCache,
+ const std::vector<int> *const sampledInputIndice, std::vector<float> *sampledSpeedRates,
+ std::vector<float> *sampledDirections) {
+ // Relative speed calculation.
+ const int sumDuration = sampledInputTimes->back() - sampledInputTimes->front();
+ const int sumLength = sampledLengthCache->back() - sampledLengthCache->front();
+ const float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
+ sampledSpeedRates->resize(sampledInputSize);
+ for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
+ const int index = (*sampledInputIndice)[i];
+ int length = 0;
+ int duration = 0;
+
+ // Calculate velocity by using distances and durations of
+ // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
+ static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2;
+ for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
+ ++j) {
+ if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) {
+ break;
+ }
+ length += getDistanceInt(xCoordinates[j], yCoordinates[j],
+ xCoordinates[j + 1], yCoordinates[j + 1]);
+ duration += times[j + 1] - times[j];
+ }
+ for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
+ if (i > 0 && j < (*sampledInputIndice)[i - 1]) {
+ break;
+ }
+ // TODO: use mSampledLengthCache instead?
+ length += getDistanceInt(xCoordinates[j], yCoordinates[j],
+ xCoordinates[j + 1], yCoordinates[j + 1]);
+ duration += times[j + 1] - times[j];
+ }
+ if (duration == 0 || sumDuration == 0) {
+ // Cannot calculate speed; thus, it gives an average value (1.0);
+ (*sampledSpeedRates)[i] = 1.0f;
+ } else {
+ const float speed = static_cast<float>(length) / static_cast<float>(duration);
+ (*sampledSpeedRates)[i] = speed / averageSpeed;
+ }
+ }
+
+ // Direction calculation.
+ sampledDirections->resize(sampledInputSize - 1);
+ for (int i = max(0, lastSavedInputSize - 1); i < sampledInputSize - 1; ++i) {
+ (*sampledDirections)[i] = getDirection(sampledInputXs, sampledInputYs, i, i + 1);
+ }
+ return averageSpeed;
+}
+
+/* static */ void ProximityInfoStateUtils::refreshBeelineSpeedRates(const int mostCommonKeyWidth,
+ const float averageSpeed, const int inputSize, const int *const xCoordinates,
+ const int *const yCoordinates, const int *times, const int sampledInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const std::vector<int> *const inputIndice,
+ std::vector<int> *beelineSpeedPercentiles) {
+ if (DEBUG_SAMPLING_POINTS) {
+ AKLOGI("--- refresh beeline speed rates");
+ }
+ beelineSpeedPercentiles->resize(sampledInputSize);
+ for (int i = 0; i < sampledInputSize; ++i) {
+ (*beelineSpeedPercentiles)[i] = static_cast<int>(calculateBeelineSpeedRate(
+ mostCommonKeyWidth, averageSpeed, i, inputSize, xCoordinates, yCoordinates, times,
+ sampledInputSize, sampledInputXs, sampledInputYs, inputIndice) * MAX_PERCENTILE);
+ }
+}
+
+/* static */float ProximityInfoStateUtils::getDirection(
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int index0, const int index1) {
+ ASSERT(sampledInputXs && sampledInputYs);
+ const int sampledInputSize =sampledInputXs->size();
+ if (index0 < 0 || index0 > sampledInputSize - 1) {
+ return 0.0f;
+ }
+ if (index1 < 0 || index1 > sampledInputSize - 1) {
+ return 0.0f;
+ }
+ const int x1 = (*sampledInputXs)[index0];
+ const int y1 = (*sampledInputYs)[index0];
+ const int x2 = (*sampledInputXs)[index1];
+ const int y2 = (*sampledInputYs)[index1];
+ return getAngle(x1, y1, x2, y2);
+}
+
+// Calculating point to key distance for all near keys and returning the distance between
+// the given point and the nearest key position.
+/* static */ float ProximityInfoStateUtils::updateNearKeysDistances(
+ const ProximityInfo *const proximityInfo, const float maxPointToKeyLength, const int x,
+ const int y, NearKeysDistanceMap *const currentNearKeysDistances) {
+ static const float NEAR_KEY_THRESHOLD = 2.0f;
+
+ currentNearKeysDistances->clear();
+ const int keyCount = proximityInfo->getKeyCount();
+ float nearestKeyDistance = maxPointToKeyLength;
+ for (int k = 0; k < keyCount; ++k) {
+ const float dist = proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
+ if (dist < NEAR_KEY_THRESHOLD) {
+ currentNearKeysDistances->insert(std::pair<int, float>(k, dist));
+ }
+ if (nearestKeyDistance > dist) {
+ nearestKeyDistance = dist;
+ }
+ }
+ return nearestKeyDistance;
+}
+
+// Check if previous point is at local minimum position to near keys.
+/* static */ bool ProximityInfoStateUtils::isPrevLocalMin(
+ const NearKeysDistanceMap *const currentNearKeysDistances,
+ const NearKeysDistanceMap *const prevNearKeysDistances,
+ const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
+ static const float MARGIN = 0.01f;
+
+ for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin();
+ it != prevNearKeysDistances->end(); ++it) {
+ NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first);
+ NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first);
+ if ((itPP == prevPrevNearKeysDistances->end() || itPP->second > it->second + MARGIN)
+ && (itC == currentNearKeysDistances->end() || itC->second > it->second + MARGIN)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Calculating a point score that indicates usefulness of the point.
+/* static */ float ProximityInfoStateUtils::getPointScore(const int mostCommonKeyWidth,
+ const int x, const int y, const int time, const bool lastPoint, const float nearest,
+ const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
+ const NearKeysDistanceMap *const prevNearKeysDistances,
+ const NearKeysDistanceMap *const prevPrevNearKeysDistances,
+ std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs) {
+ static const int DISTANCE_BASE_SCALE = 100;
+ static const float NEAR_KEY_THRESHOLD = 0.6f;
+ static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 25;
+ static const float NOT_LOCALMIN_DISTANCE_SCORE = -1.0f;
+ static const float LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE = 1.0f;
+ static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 2.0f / 3.0f;
+ static const float CORNER_SUM_ANGLE_THRESHOLD = M_PI_F / 4.0f;
+ static const float CORNER_SCORE = 1.0f;
+
+ const size_t size = sampledInputXs->size();
+ // If there is only one point, add this point. Besides, if the previous point's distance map
+ // is empty, we re-compute nearby keys distances from the current point.
+ // Note that the current point is the first point in the incremental input that needs to
+ // be re-computed.
+ if (size <= 1 || prevNearKeysDistances->empty()) {
+ return 0.0f;
+ }
+
+ const int baseSampleRate = mostCommonKeyWidth;
+ const int distPrev = getDistanceInt(sampledInputXs->back(), sampledInputYs->back(),
+ (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]) * DISTANCE_BASE_SCALE;
+ float score = 0.0f;
+
+ // Location
+ if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
+ prevPrevNearKeysDistances)) {
+ score += NOT_LOCALMIN_DISTANCE_SCORE;
+ } else if (nearest < NEAR_KEY_THRESHOLD) {
+ // Promote points nearby keys
+ score += LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE;
+ }
+ // Angle
+ const float angle1 = getAngle(x, y, sampledInputXs->back(), sampledInputYs->back());
+ const float angle2 = getAngle(sampledInputXs->back(), sampledInputYs->back(),
+ (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]);
+ const float angleDiff = getAngleDiff(angle1, angle2);
+
+ // Save corner
+ if (distPrev > baseSampleRate * CORNER_CHECK_DISTANCE_THRESHOLD_SCALE
+ && (sumAngle > CORNER_SUM_ANGLE_THRESHOLD || angleDiff > CORNER_ANGLE_THRESHOLD)) {
+ score += CORNER_SCORE;
+ }
+ return score;
+}
+
+// Sampling touch point and pushing information to vectors.
+// Returning if previous point is popped or not.
+/* static */ bool ProximityInfoStateUtils::pushTouchPoint(const int mostCommonKeyWidth,
+ const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
+ const int inputIndex, const int nodeCodePoint, int x, int y,
+ const int time, const bool doSampling, const bool isLastPoint, const float sumAngle,
+ NearKeysDistanceMap *const currentNearKeysDistances,
+ const NearKeysDistanceMap *const prevNearKeysDistances,
+ const NearKeysDistanceMap *const prevPrevNearKeysDistances,
+ std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
+ std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
+ std::vector<int> *sampledInputIndice) {
+ static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
+
+ size_t size = sampledInputXs->size();
+ bool popped = false;
+ if (nodeCodePoint < 0 && doSampling) {
+ const float nearest = updateNearKeysDistances(
+ proximityInfo, maxPointToKeyLength, x, y, currentNearKeysDistances);
+ const float score = getPointScore(mostCommonKeyWidth, x, y, time, isLastPoint, nearest,
+ sumAngle, currentNearKeysDistances, prevNearKeysDistances,
+ prevPrevNearKeysDistances, sampledInputXs, sampledInputYs);
+ if (score < 0) {
+ // Pop previous point because it would be useless.
+ popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
+ sampledInputIndice);
+ size = sampledInputXs->size();
+ popped = true;
+ } else {
+ popped = false;
+ }
+ // Check if the last point should be skipped.
+ if (isLastPoint && size > 0) {
+ if (getDistanceInt(x, y, sampledInputXs->back(),
+ sampledInputYs->back()) * LAST_POINT_SKIP_DISTANCE_SCALE
+ < mostCommonKeyWidth) {
+ // This point is not used because it's too close to the previous point.
+ if (DEBUG_GEO_FULL) {
+ AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
+ "width = %d", size, x, y, sampledInputXs->back(),
+ sampledInputYs->back(), getDistanceInt(
+ x, y, sampledInputXs->back(), sampledInputYs->back()),
+ mostCommonKeyWidth / LAST_POINT_SKIP_DISTANCE_SCALE);
+ }
+ return popped;
+ }
+ }
+ }
+
+ if (nodeCodePoint >= 0 && (x < 0 || y < 0)) {
+ const int keyId = proximityInfo->getKeyIndexOf(nodeCodePoint);
+ if (keyId >= 0) {
+ x = proximityInfo->getKeyCenterXOfKeyIdG(keyId);
+ y = proximityInfo->getKeyCenterYOfKeyIdG(keyId);
+ }
+ }
+
+ // Pushing point information.
+ if (size > 0) {
+ sampledLengthCache->push_back(
+ sampledLengthCache->back() + getDistanceInt(
+ x, y, sampledInputXs->back(), sampledInputYs->back()));
+ } else {
+ sampledLengthCache->push_back(0);
+ }
+ sampledInputXs->push_back(x);
+ sampledInputYs->push_back(y);
+ sampledInputTimes->push_back(time);
+ sampledInputIndice->push_back(inputIndex);
+ if (DEBUG_GEO_FULL) {
+ AKLOGI("pushTouchPoint: x = %03d, y = %03d, time = %d, index = %d, popped ? %01d",
+ x, y, time, inputIndex, popped);
+ }
+ return popped;
+}
+
+/* static */ float ProximityInfoStateUtils::calculateBeelineSpeedRate(const int mostCommonKeyWidth,
+ const float averageSpeed, const int id, const int inputSize, const int *const xCoordinates,
+ const int *const yCoordinates, const int *times, const int sampledInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<int> *const sampledInputIndices) {
+ if (sampledInputSize <= 0 || averageSpeed < 0.001f) {
+ if (DEBUG_SAMPLING_POINTS) {
+ AKLOGI("--- invalid state: cancel. size = %d, ave = %f",
+ sampledInputSize, averageSpeed);
+ }
+ return 1.0f;
+ }
+ const int lookupRadius = mostCommonKeyWidth
+ * ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE;
+ const int x0 = (*sampledInputXs)[id];
+ const int y0 = (*sampledInputYs)[id];
+ const int actualInputIndex = (*sampledInputIndices)[id];
+ int tempTime = 0;
+ int tempBeelineDistance = 0;
+ int start = actualInputIndex;
+ // lookup forward
+ while (start > 0 && tempBeelineDistance < lookupRadius) {
+ tempTime += times[start] - times[start - 1];
+ --start;
+ tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[start], yCoordinates[start]);
+ }
+ // Exclusive unless this is an edge point
+ if (start > 0 && start < actualInputIndex) {
+ ++start;
+ }
+ tempTime= 0;
+ tempBeelineDistance = 0;
+ int end = actualInputIndex;
+ // lookup backward
+ while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) {
+ tempTime += times[end + 1] - times[end];
+ ++end;
+ tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[end], yCoordinates[end]);
+ }
+ // Exclusive unless this is an edge point
+ if (end > actualInputIndex && end < (inputSize - 1)) {
+ --end;
+ }
+
+ if (start >= end) {
+ if (DEBUG_DOUBLE_LETTER) {
+ AKLOGI("--- double letter: start == end %d", start);
+ }
+ return 1.0f;
+ }
+
+ const int x2 = xCoordinates[start];
+ const int y2 = yCoordinates[start];
+ const int x3 = xCoordinates[end];
+ const int y3 = yCoordinates[end];
+ const int beelineDistance = getDistanceInt(x2, y2, x3, y3);
+ int adjustedStartTime = times[start];
+ if (start == 0 && actualInputIndex == 0 && inputSize > 1) {
+ adjustedStartTime += ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS;
+ }
+ int adjustedEndTime = times[end];
+ if (end == (inputSize - 1) && inputSize > 1) {
+ adjustedEndTime -= ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS;
+ }
+ const int time = adjustedEndTime - adjustedStartTime;
+ if (time <= 0) {
+ return 1.0f;
+ }
+
+ if (time >= ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS){
+ return 0.0f;
+ }
+ if (DEBUG_DOUBLE_LETTER) {
+ AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d,"
+ " speed = %f, ave = %f, val = %f, start time = %d, end time = %d",
+ id, (*sampledInputIndices)[id], start, end, beelineDistance, time,
+ (static_cast<float>(beelineDistance) / static_cast<float>(time)), averageSpeed,
+ ((static_cast<float>(beelineDistance) / static_cast<float>(time))
+ / averageSpeed), adjustedStartTime, adjustedEndTime);
+ }
+ // Offset 1%
+ // TODO: Detect double letter more smartly
+ return 0.01f + static_cast<float>(beelineDistance) / static_cast<float>(time) / averageSpeed;
+}
+
+/* static */ float ProximityInfoStateUtils::getPointAngle(
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int index) {
+ if (!sampledInputXs || !sampledInputYs) {
+ return 0.0f;
+ }
+ const int sampledInputSize = sampledInputXs->size();
+ if (index <= 0 || index >= sampledInputSize - 1) {
+ return 0.0f;
+ }
+ const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index - 1, index);
+ const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index, index + 1);
+ const float directionDiff = getAngleDiff(previousDirection, nextDirection);
+ return directionDiff;
+}
+
+/* static */ float ProximityInfoStateUtils::getPointsAngle(
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const int index0, const int index1, const int index2) {
+ if (!sampledInputXs || !sampledInputYs) {
+ return 0.0f;
+ }
+ const int sampledInputSize = sampledInputXs->size();
+ if (index0 < 0 || index0 > sampledInputSize - 1) {
+ return 0.0f;
+ }
+ if (index1 < 0 || index1 > sampledInputSize - 1) {
+ return 0.0f;
+ }
+ if (index2 < 0 || index2 > sampledInputSize - 1) {
+ return 0.0f;
+ }
+ const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index0, index1);
+ const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index1, index2);
+ return getAngleDiff(previousDirection, nextDirection);
+}
+
+// TODO: Remove the "scale" parameter
+// This function basically converts from a length to an edit distance. Accordingly, it's obviously
+// wrong to compare with mMaxPointToKeyLength.
+/* static */ float ProximityInfoStateUtils::getPointToKeyByIdLength(const float maxPointToKeyLength,
+ const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
+ const int inputIndex, const int keyId, const float scale) {
+ if (keyId != NOT_AN_INDEX) {
+ const int index = inputIndex * keyCount + keyId;
+ return min((*SampledDistanceCache_G)[index] * scale, maxPointToKeyLength);
+ }
+ // If the char is not a key on the keyboard then return the max length.
+ return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+}
+
+/* static */ float ProximityInfoStateUtils::getPointToKeyByIdLength(const float maxPointToKeyLength,
+ const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
+ const int inputIndex, const int keyId) {
+ return getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, inputIndex, keyId, 1.0f);
+}
+
+// Updates probabilities of aligning to some keys and skipping.
+// Word suggestion should be based on this probabilities.
+/* static */ void ProximityInfoStateUtils::updateAlignPointProbabilities(
+ const float maxPointToKeyLength, const int mostCommonKeyWidth, const int keyCount,
+ const int start, const int sampledInputSize, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<float> *const sampledSpeedRates,
+ const std::vector<int> *const sampledLengthCache,
+ const std::vector<float> *const SampledDistanceCache_G,
+ std::vector<NearKeycodesSet> *SampledNearKeysVector,
+ std::vector<hash_map_compat<int, float> > *charProbabilities) {
+ static const float MIN_PROBABILITY = 0.000001f;
+ static const float MAX_SKIP_PROBABILITY = 0.95f;
+ static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
+ static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
+ static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
+ static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
+ static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
+ static const float NEAREST_DISTANCE_WEIGHT = 0.5f;
+ static const float NEAREST_DISTANCE_BIAS = 0.5f;
+ static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
+ static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
+
+ static const float ANGLE_WEIGHT = 0.90f;
+ static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
+ static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
+ static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
+ static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
+ static const float SKIP_CORNER_PROBABILITY = 0.4f;
+ static const float SPEED_MARGIN = 0.1f;
+ static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
+
+ charProbabilities->resize(sampledInputSize);
+ // Calculates probabilities of using a point as a correlated point with the character
+ // for each point.
+ for (int i = start; i < sampledInputSize; ++i) {
+ (*charProbabilities)[i].clear();
+ // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY.
+ // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
+ float skipProbability = MAX_SKIP_PROBABILITY;
+
+ const float currentAngle = getPointAngle(sampledInputXs, sampledInputYs, i);
+ const float speedRate = (*sampledSpeedRates)[i];
+
+ float nearestKeyDistance = static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+ for (int j = 0; j < keyCount; ++j) {
+ if ((*SampledNearKeysVector)[i].test(j)) {
+ const float distance = getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i, j);
+ if (distance < nearestKeyDistance) {
+ nearestKeyDistance = distance;
+ }
+ }
+ }
+
+ if (i == 0) {
+ skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT
+ + NEAREST_DISTANCE_BIAS);
+ // Promote the first point
+ skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
+ } else if (i == sampledInputSize - 1) {
+ skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST
+ + NEAREST_DISTANCE_BIAS_FOR_LAST);
+ // Promote the last point
+ skipProbability *= SKIP_LAST_POINT_PROBABILITY;
+ } else {
+ // If the current speed is relatively slower than adjacent keys, we promote this point.
+ if ((*sampledSpeedRates)[i - 1] - SPEED_MARGIN > speedRate
+ && speedRate < (*sampledSpeedRates)[i + 1] - SPEED_MARGIN) {
+ if (currentAngle < CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= min(1.0f, speedRate
+ * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
+ } else {
+ // If the angle is small enough, we promote this point more. (e.g. pit vs put)
+ skipProbability *= min(1.0f, speedRate * SPEED_WEIGHT_FOR_SKIP_PROBABILITY
+ + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
+ }
+ }
+
+ skipProbability *= min(1.0f, speedRate * nearestKeyDistance *
+ NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS);
+
+ // Adjusts skip probability by a rate depending on angle.
+ // ANGLE_RATE of skipProbability is adjusted by current angle.
+ skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT
+ + (1.0f - ANGLE_WEIGHT);
+ if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
+ }
+ // We assume the angle of this point is the angle for point[i], point[i - 2]
+ // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
+ // and point[i - 2] is this angle can be more affected by the noise.
+ const float prevAngle = getPointsAngle(sampledInputXs, sampledInputYs, i, i - 2, i - 3);
+ if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD
+ && currentAngle > CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= SKIP_CORNER_PROBABILITY;
+ }
+ }
+
+ // probabilities must be in [0.0, MAX_SKIP_PROBABILITY];
+ ASSERT(skipProbability >= 0.0f);
+ ASSERT(skipProbability <= MAX_SKIP_PROBABILITY);
+ (*charProbabilities)[i][NOT_AN_INDEX] = skipProbability;
+
+ // Second, calculates key probabilities by dividing the rest probability
+ // (1.0f - skipProbability).
+ const float inputCharProbability = 1.0f - skipProbability;
+
+ // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
+ // learning or something would be efficient.
+ static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
+ static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
+ static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
+ static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
+ static const float MIN_STANDERD_DIVIATION = 0.37f;
+
+ const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F
+ * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
+ MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
+ const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance
+ * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
+ MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
+ const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION;
+
+ ProximityInfoUtils::NormalDistribution
+ distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
+ static const float PREV_DISTANCE_WEIGHT = 0.5f;
+ static const float NEXT_DISTANCE_WEIGHT = 0.6f;
+ // Summing up probability densities of all near keys.
+ float sumOfProbabilityDensities = 0.0f;
+ for (int j = 0; j < keyCount; ++j) {
+ if ((*SampledNearKeysVector)[i].test(j)) {
+ float distance = sqrtf(getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i, j));
+ if (i == 0 && i != sampledInputSize - 1) {
+ // For the first point, weighted average of distances from first point and the
+ // next point to the key is used as a point to key distance.
+ const float nextDistance = sqrtf(getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i + 1, j));
+ if (nextDistance < distance) {
+ // The distance of the first point tends to bigger than continuing
+ // points because the first touch by the user can be sloppy.
+ // So we promote the first point if the distance of that point is larger
+ // than the distance of the next point.
+ distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT)
+ / (1.0f + NEXT_DISTANCE_WEIGHT);
+ }
+ } else if (i != 0 && i == sampledInputSize - 1) {
+ // For the first point, weighted average of distances from last point and
+ // the previous point to the key is used as a point to key distance.
+ const float previousDistance = sqrtf(getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i - 1, j));
+ if (previousDistance < distance) {
+ // The distance of the last point tends to bigger than continuing points
+ // because the last touch by the user can be sloppy. So we promote the
+ // last point if the distance of that point is larger than the distance of
+ // the previous point.
+ distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT)
+ / (1.0f + PREV_DISTANCE_WEIGHT);
+ }
+ }
+ // TODO: Promote the first point when the extended line from the next input is near
+ // from a key. Also, promote the last point as well.
+ sumOfProbabilityDensities += distribution.getProbabilityDensity(distance);
+ }
+ }
+
+ // Split the probability of an input point to keys that are close to the input point.
+ for (int j = 0; j < keyCount; ++j) {
+ if ((*SampledNearKeysVector)[i].test(j)) {
+ float distance = sqrtf(getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i, j));
+ if (i == 0 && i != sampledInputSize - 1) {
+ // For the first point, weighted average of distances from the first point and
+ // the next point to the key is used as a point to key distance.
+ const float prevDistance = sqrtf(getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i + 1, j));
+ if (prevDistance < distance) {
+ distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT)
+ / (1.0f + NEXT_DISTANCE_WEIGHT);
+ }
+ } else if (i != 0 && i == sampledInputSize - 1) {
+ // For the first point, weighted average of distances from last point and
+ // the previous point to the key is used as a point to key distance.
+ const float prevDistance = sqrtf(getPointToKeyByIdLength(
+ maxPointToKeyLength, SampledDistanceCache_G, keyCount, i - 1, j));
+ if (prevDistance < distance) {
+ distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT)
+ / (1.0f + PREV_DISTANCE_WEIGHT);
+ }
+ }
+ const float probabilityDensity = distribution.getProbabilityDensity(distance);
+ const float probability = inputCharProbability * probabilityDensity
+ / sumOfProbabilityDensities;
+ (*charProbabilities)[i][j] = probability;
+ }
+ }
+ }
+
+ if (DEBUG_POINTS_PROBABILITY) {
+ for (int i = 0; i < sampledInputSize; ++i) {
+ std::stringstream sstream;
+ sstream << i << ", ";
+ sstream << "(" << (*sampledInputXs)[i] << ", " << (*sampledInputYs)[i] << "), ";
+ sstream << "Speed: "<< (*sampledSpeedRates)[i] << ", ";
+ sstream << "Angle: "<< getPointAngle(sampledInputXs, sampledInputYs, i) << ", \n";
+
+ for (hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].begin();
+ it != (*charProbabilities)[i].end(); ++it) {
+ if (it->first == NOT_AN_INDEX) {
+ sstream << it->first
+ << "(skip):"
+ << it->second
+ << "\n";
+ } else {
+ sstream << it->first
+ << "("
+ //<< static_cast<char>(mProximityInfo->getCodePointOf(it->first))
+ << "):"
+ << it->second
+ << "\n";
+ }
+ }
+ AKLOGI("%s", sstream.str().c_str());
+ }
+ }
+
+ // Decrease key probabilities of points which don't have the highest probability of that key
+ // among nearby points. Probabilities of the first point and the last point are not suppressed.
+ for (int i = max(start, 1); i < sampledInputSize; ++i) {
+ for (int j = i + 1; j < sampledInputSize; ++j) {
+ if (!suppressCharProbabilities(
+ mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j,
+ charProbabilities)) {
+ break;
+ }
+ }
+ for (int j = i - 1; j >= max(start, 0); --j) {
+ if (!suppressCharProbabilities(
+ mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j,
+ charProbabilities)) {
+ break;
+ }
+ }
+ }
+
+ // Converting from raw probabilities to log probabilities to calculate spatial distance.
+ for (int i = start; i < sampledInputSize; ++i) {
+ for (int j = 0; j < keyCount; ++j) {
+ hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j);
+ if (it == (*charProbabilities)[i].end()){
+ (*SampledNearKeysVector)[i].reset(j);
+ } else if(it->second < MIN_PROBABILITY) {
+ // Erases from near keys vector because it has very low probability.
+ (*SampledNearKeysVector)[i].reset(j);
+ (*charProbabilities)[i].erase(j);
+ } else {
+ it->second = -logf(it->second);
+ }
+ }
+ (*charProbabilities)[i][NOT_AN_INDEX] = -logf((*charProbabilities)[i][NOT_AN_INDEX]);
+ }
+}
+
+/* static */ void ProximityInfoStateUtils::updateSampledSearchKeysVector(
+ const ProximityInfo *const proximityInfo, const int sampledInputSize,
+ const int lastSavedInputSize,
+ const std::vector<int> *const sampledLengthCache,
+ const std::vector<NearKeycodesSet> *const SampledNearKeysVector,
+ std::vector<NearKeycodesSet> *sampledSearchKeysVector) {
+ sampledSearchKeysVector->resize(sampledInputSize);
+ const int readForwordLength = static_cast<int>(
+ hypotf(proximityInfo->getKeyboardWidth(), proximityInfo->getKeyboardHeight())
+ * ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO);
+ for (int i = 0; i < sampledInputSize; ++i) {
+ if (i >= lastSavedInputSize) {
+ (*sampledSearchKeysVector)[i].reset();
+ }
+ for (int j = max(i, lastSavedInputSize); j < sampledInputSize; ++j) {
+ // TODO: Investigate if this is required. This may not fail.
+ if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) {
+ break;
+ }
+ (*sampledSearchKeysVector)[i] |= (*SampledNearKeysVector)[j];
+ }
+ }
+}
+
+// Decreases char probabilities of index0 by checking probabilities of a near point (index1) and
+// increases char probabilities of index1 by checking probabilities of index0.
+/* static */ bool ProximityInfoStateUtils::suppressCharProbabilities(const int mostCommonKeyWidth,
+ const int sampledInputSize, const std::vector<int> *const lengthCache,
+ const int index0, const int index1,
+ std::vector<hash_map_compat<int, float> > *charProbabilities) {
+ ASSERT(0 <= index0 && index0 < sampledInputSize);
+ ASSERT(0 <= index1 && index1 < sampledInputSize);
+
+ static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
+ static const float MIN_SUPPRESSION_RATE = 0.1f;
+ static const float SUPPRESSION_WEIGHT = 0.5f;
+ static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
+ static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
+
+ const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth);
+ const float diff = fabsf(static_cast<float>((*lengthCache)[index0] - (*lengthCache)[index1]));
+ if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
+ return false;
+ }
+ const float suppressionRate = MIN_SUPPRESSION_RATE
+ + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT;
+ for (hash_map_compat<int, float>::iterator it = (*charProbabilities)[index0].begin();
+ it != (*charProbabilities)[index0].end(); ++it) {
+ hash_map_compat<int, float>::iterator it2 = (*charProbabilities)[index1].find(it->first);
+ if (it2 != (*charProbabilities)[index1].end() && it->second < it2->second) {
+ const float newProbability = it->second * suppressionRate;
+ const float suppression = it->second - newProbability;
+ it->second = newProbability;
+ // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point.
+ (*charProbabilities)[index0][NOT_AN_INDEX] += suppression;
+
+ // Add the probability of the same key nearby index1
+ const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
+ (*charProbabilities)[index1][NOT_AN_INDEX]
+ * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
+ it2->second += probabilityGain;
+ (*charProbabilities)[index1][NOT_AN_INDEX] -= probabilityGain;
+ }
+ }
+ return true;
+}
+
+/* static */ void ProximityInfoStateUtils::dump(const bool isGeometric, const int inputSize,
+ const int *const inputXCoordinates, const int *const inputYCoordinates,
+ const int sampledInputSize, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<int> *const sampledTimes,
+ const std::vector<float> *const sampledSpeedRates,
+ const std::vector<int> *const sampledBeelineSpeedPercentiles) {
+ if (DEBUG_GEO_FULL) {
+ for (int i = 0; i < sampledInputSize; ++i) {
+ AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, (*sampledInputXs)[i],
+ (*sampledInputYs)[i], sampledTimes ? (*sampledTimes)[i] : -1);
+ }
+ }
+
+ std::stringstream originalX, originalY, sampledX, sampledY;
+ for (int i = 0; i < inputSize; ++i) {
+ originalX << inputXCoordinates[i];
+ originalY << inputYCoordinates[i];
+ if (i != inputSize - 1) {
+ originalX << ";";
+ originalY << ";";
+ }
+ }
+ AKLOGI("===== sampled points =====");
+ for (int i = 0; i < sampledInputSize; ++i) {
+ if (isGeometric) {
+ AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d",
+ i, (*sampledInputXs)[i], (*sampledInputYs)[i], (*sampledTimes)[i],
+ (*sampledSpeedRates)[i], (*sampledBeelineSpeedPercentiles)[i]);
+ }
+ sampledX << (*sampledInputXs)[i];
+ sampledY << (*sampledInputYs)[i];
+ if (i != sampledInputSize - 1) {
+ sampledX << ";";
+ sampledY << ";";
+ }
+ }
+ AKLOGI("original points:\n%s, %s,\nsampled points:\n%s, %s,\n",
+ originalX.str().c_str(), originalY.str().c_str(), sampledX.str().c_str(),
+ sampledY.str().c_str());
+}
+} // namespace latinime
diff --git a/native/jni/src/proximity_info_state_utils.h b/native/jni/src/proximity_info_state_utils.h
index 53b992a..17ef1c3 100644
--- a/native/jni/src/proximity_info_state_utils.h
+++ b/native/jni/src/proximity_info_state_utils.h
@@ -17,16 +17,21 @@
#ifndef LATINIME_PROXIMITY_INFO_STATE_UTILS_H
#define LATINIME_PROXIMITY_INFO_STATE_UTILS_H
+#include <bitset>
#include <vector>
#include "defines.h"
-#include "geometry_utils.h"
#include "hash_map_compat.h"
-#include "proximity_info.h"
namespace latinime {
+class ProximityInfo;
+class ProximityInfoParams;
+
class ProximityInfoStateUtils {
public:
+ typedef hash_map_compat<int, float> NearKeysDistanceMap;
+ typedef std::bitset<MAX_KEY_COUNT_IN_A_KEYBOARD> NearKeycodesSet;
+
static int updateTouchPoints(const int mostCommonKeyWidth,
const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
const int *const inputProximities,
@@ -35,285 +40,122 @@
const bool isGeometric, const int pointerId, const int pushTouchPointStartIndex,
std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
- std::vector<int> *sampledInputIndice) {
- if (DEBUG_SAMPLING_POINTS) {
- if (times) {
- for (int i = 0; i < inputSize; ++i) {
- AKLOGI("(%d) x %d, y %d, time %d",
- i, xCoordinates[i], yCoordinates[i], times[i]);
- }
- }
- }
-#ifdef DO_ASSERT_TEST
- if (times) {
- for (int i = 0; i < inputSize; ++i) {
- if (i > 0) {
- ASSERT(times[i] >= times[i - 1]);
- }
- }
- }
-#endif
- const bool proximityOnly = !isGeometric
- && (inputXCoordinates[0] < 0 || inputYCoordinates[0] < 0);
- int lastInputIndex = pushTouchPointStartIndex;
- for (int i = lastInputIndex; i < inputSize; ++i) {
- const int pid = pointerIds ? pointerIds[i] : 0;
- if (pointerId == pid) {
- lastInputIndex = i;
- }
- }
- if (DEBUG_GEO_FULL) {
- AKLOGI("Init ProximityInfoState: last input index = %d", lastInputIndex);
- }
- // Working space to save near keys distances for current, prev and prevprev input point.
- NearKeysDistanceMap nearKeysDistances[3];
- // These pointers are swapped for each inputs points.
- NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0];
- NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1];
- NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2];
- // "sumAngle" is accumulated by each angle of input points. And when "sumAngle" exceeds
- // the threshold we save that point, reset sumAngle. This aims to keep the figure of
- // the curve.
- float sumAngle = 0.0f;
-
- for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) {
- // Assuming pointerId == 0 if pointerIds is null.
- const int pid = pointerIds ? pointerIds[i] : 0;
- if (DEBUG_GEO_FULL) {
- AKLOGI("Init ProximityInfoState: (%d)PID = %d", i, pid);
- }
- if (pointerId == pid) {
- const int c = isGeometric ?
- NOT_A_COORDINATE : getPrimaryCodePointAt(inputProximities, i);
- const int x = proximityOnly ? NOT_A_COORDINATE : inputXCoordinates[i];
- const int y = proximityOnly ? NOT_A_COORDINATE : inputYCoordinates[i];
- const int time = times ? times[i] : -1;
-
- if (i > 1) {
- const float prevAngle = getAngle(
- inputXCoordinates[i - 2], inputYCoordinates[i - 2],
- inputXCoordinates[i - 1], inputYCoordinates[i - 1]);
- const float currentAngle =
- getAngle(inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
- sumAngle += getAngleDiff(prevAngle, currentAngle);
- }
-
- if (pushTouchPoint(mostCommonKeyWidth, proximityInfo, maxPointToKeyLength,
- i, c, x, y, time, isGeometric /* do sampling */,
- i == lastInputIndex, sumAngle, currentNearKeysDistances,
- prevNearKeysDistances, prevPrevNearKeysDistances,
- sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
- sampledInputIndice)) {
- // Previous point information was popped.
- NearKeysDistanceMap *tmp = prevNearKeysDistances;
- prevNearKeysDistances = currentNearKeysDistances;
- currentNearKeysDistances = tmp;
- } else {
- NearKeysDistanceMap *tmp = prevPrevNearKeysDistances;
- prevPrevNearKeysDistances = prevNearKeysDistances;
- prevNearKeysDistances = currentNearKeysDistances;
- currentNearKeysDistances = tmp;
- sumAngle = 0.0f;
- }
- }
- }
- return sampledInputXs->size();
- }
-
- static const int *getProximityCodePointsAt(
- const int *const inputProximities, const int index) {
- return inputProximities + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL);
- }
-
- static int getPrimaryCodePointAt(const int *const inputProximities, const int index) {
- return getProximityCodePointsAt(inputProximities, index)[0];
- }
-
+ std::vector<int> *sampledInputIndice);
+ static const int *getProximityCodePointsAt(const int *const inputProximities, const int index);
+ static int getPrimaryCodePointAt(const int *const inputProximities, const int index);
static void popInputData(std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
- std::vector<int> *sampledInputIndice) {
- sampledInputXs->pop_back();
- sampledInputYs->pop_back();
- sampledInputTimes->pop_back();
- sampledLengthCache->pop_back();
- sampledInputIndice->pop_back();
- }
-
+ std::vector<int> *sampledInputIndice);
+ static float refreshSpeedRates(const int inputSize, const int *const xCoordinates,
+ const int *const yCoordinates, const int *const times, const int lastSavedInputSize,
+ const int sampledInputSize, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<int> *const sampledInputTimes,
+ const std::vector<int> *const sampledLengthCache,
+ const std::vector<int> *const sampledInputIndice,
+ std::vector<float> *sampledSpeedRates, std::vector<float> *sampledDirections);
+ static void refreshBeelineSpeedRates(const int mostCommonKeyWidth, const float averageSpeed,
+ const int inputSize, const int *const xCoordinates, const int *const yCoordinates,
+ const int *times, const int sampledInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const std::vector<int> *const inputIndice,
+ std::vector<int> *beelineSpeedPercentiles);
+ static float getDirection(const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int index0, const int index1);
+ static void updateAlignPointProbabilities(
+ const float maxPointToKeyLength, const int mostCommonKeyWidth, const int keyCount,
+ const int start, const int sampledInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<float> *const sampledSpeedRates,
+ const std::vector<int> *const sampledLengthCache,
+ const std::vector<float> *const SampledDistanceCache_G,
+ std::vector<NearKeycodesSet> *SampledNearKeysVector,
+ std::vector<hash_map_compat<int, float> > *charProbabilities);
+ static void updateSampledSearchKeysVector(
+ const ProximityInfo *const proximityInfo, const int sampledInputSize,
+ const int lastSavedInputSize,
+ const std::vector<int> *const sampledLengthCache,
+ const std::vector<NearKeycodesSet> *const SampledNearKeysVector,
+ std::vector<NearKeycodesSet> *sampledSearchKeysVector);
+ static float getPointToKeyByIdLength(const float maxPointToKeyLength,
+ const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
+ const int inputIndex, const int keyId, const float scale);
+ static float getPointToKeyByIdLength(const float maxPointToKeyLength,
+ const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
+ const int inputIndex, const int keyId);
+ static void initGeometricDistanceInfos(
+ const ProximityInfo *const proximityInfo, const int keyCount,
+ const int sampledInputSize, const int lastSavedInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ std::vector<NearKeycodesSet> *SampledNearKeysVector,
+ std::vector<float> *SampledDistanceCache_G);
+ static void initPrimaryInputWord(
+ const int inputSize, const int *const inputProximities, int *primaryInputWord);
+ static void initNormalizedSquaredDistances(
+ const ProximityInfo *const proximityInfo, const int inputSize,
+ const int *inputXCoordinates, const int *inputYCoordinates,
+ const int *const inputProximities, const bool hasInputCoordinates,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ int *normalizedSquaredDistances);
+ static void dump(const bool isGeometric, const int inputSize,
+ const int *const inputXCoordinates, const int *const inputYCoordinates,
+ const int sampledInputSize, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<int> *const sampledTimes,
+ const std::vector<float> *const sampledSpeedRates,
+ const std::vector<int> *const sampledBeelineSpeedPercentiles);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfoStateUtils);
- typedef hash_map_compat<int, float> NearKeysDistanceMap;
-
- // Calculating point to key distance for all near keys and returning the distance between
- // the given point and the nearest key position.
static float updateNearKeysDistances(const ProximityInfo *const proximityInfo,
const float maxPointToKeyLength, const int x, const int y,
- NearKeysDistanceMap *const currentNearKeysDistances) {
- static const float NEAR_KEY_THRESHOLD = 2.0f;
-
- currentNearKeysDistances->clear();
- const int keyCount = proximityInfo->getKeyCount();
- float nearestKeyDistance = maxPointToKeyLength;
- for (int k = 0; k < keyCount; ++k) {
- const float dist = proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
- if (dist < NEAR_KEY_THRESHOLD) {
- currentNearKeysDistances->insert(std::pair<int, float>(k, dist));
- }
- if (nearestKeyDistance > dist) {
- nearestKeyDistance = dist;
- }
- }
- return nearestKeyDistance;
- }
-
- // Check if previous point is at local minimum position to near keys.
+ NearKeysDistanceMap *const currentNearKeysDistances);
static bool isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
- const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
- static const float MARGIN = 0.01f;
-
- for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin();
- it != prevNearKeysDistances->end(); ++it) {
- NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first);
- NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first);
- if ((itPP == prevPrevNearKeysDistances->end() || itPP->second > it->second + MARGIN)
- && (itC == currentNearKeysDistances->end()
- || itC->second > it->second + MARGIN)) {
- return true;
- }
- }
- return false;
- }
-
- // Calculating a point score that indicates usefulness of the point.
- static float getPointScore(const int mostCommonKeyWidth,
- const int x, const int y, const int time, const bool lastPoint, const float nearest,
- const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
+ const NearKeysDistanceMap *const prevPrevNearKeysDistances);
+ static float getPointScore(const int mostCommonKeyWidth, const int x, const int y,
+ const int time, const bool lastPoint, const float nearest, const float sumAngle,
+ const NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances,
- std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs) {
- static const int DISTANCE_BASE_SCALE = 100;
- static const float NEAR_KEY_THRESHOLD = 0.6f;
- static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 25;
- static const float NOT_LOCALMIN_DISTANCE_SCORE = -1.0f;
- static const float LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE = 1.0f;
- static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 2.0f / 3.0f;
- static const float CORNER_SUM_ANGLE_THRESHOLD = M_PI_F / 4.0f;
- static const float CORNER_SCORE = 1.0f;
-
- const size_t size = sampledInputXs->size();
- // If there is only one point, add this point. Besides, if the previous point's distance map
- // is empty, we re-compute nearby keys distances from the current point.
- // Note that the current point is the first point in the incremental input that needs to
- // be re-computed.
- if (size <= 1 || prevNearKeysDistances->empty()) {
- return 0.0f;
- }
-
- const int baseSampleRate = mostCommonKeyWidth;
- const int distPrev = getDistanceInt(
- sampledInputXs->back(), sampledInputYs->back(),
- (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]) * DISTANCE_BASE_SCALE;
- float score = 0.0f;
-
- // Location
- if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
- prevPrevNearKeysDistances)) {
- score += NOT_LOCALMIN_DISTANCE_SCORE;
- } else if (nearest < NEAR_KEY_THRESHOLD) {
- // Promote points nearby keys
- score += LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE;
- }
- // Angle
- const float angle1 = getAngle(x, y, sampledInputXs->back(), sampledInputYs->back());
- const float angle2 = getAngle(sampledInputXs->back(), sampledInputYs->back(),
- (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]);
- const float angleDiff = getAngleDiff(angle1, angle2);
-
- // Save corner
- if (distPrev > baseSampleRate * CORNER_CHECK_DISTANCE_THRESHOLD_SCALE
- && (sumAngle > CORNER_SUM_ANGLE_THRESHOLD || angleDiff > CORNER_ANGLE_THRESHOLD)) {
- score += CORNER_SCORE;
- }
- return score;
- }
-
- // Sampling touch point and pushing information to vectors.
- // Returning if previous point is popped or not.
+ std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs);
static bool pushTouchPoint(const int mostCommonKeyWidth,
const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
- const int inputIndex, const int nodeCodePoint, int x, int y,
- const int time, const bool sample, const bool isLastPoint, const float sumAngle,
+ const int inputIndex, const int nodeCodePoint, int x, int y, const int time,
+ const bool doSampling, const bool isLastPoint, const float sumAngle,
NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances,
std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
- std::vector<int> *sampledInputIndice) {
- static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
-
- size_t size = sampledInputXs->size();
- bool popped = false;
- if (nodeCodePoint < 0 && sample) {
- const float nearest = updateNearKeysDistances(
- proximityInfo, maxPointToKeyLength, x, y, currentNearKeysDistances);
- const float score = getPointScore(mostCommonKeyWidth, x, y, time, isLastPoint, nearest,
- sumAngle, currentNearKeysDistances, prevNearKeysDistances,
- prevPrevNearKeysDistances, sampledInputXs, sampledInputYs);
- if (score < 0) {
- // Pop previous point because it would be useless.
- popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
- sampledInputIndice);
- size = sampledInputXs->size();
- popped = true;
- } else {
- popped = false;
- }
- // Check if the last point should be skipped.
- if (isLastPoint && size > 0) {
- if (getDistanceInt(x, y, sampledInputXs->back(),
- sampledInputYs->back()) * LAST_POINT_SKIP_DISTANCE_SCALE
- < mostCommonKeyWidth) {
- // This point is not used because it's too close to the previous point.
- if (DEBUG_GEO_FULL) {
- AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
- "width = %d", size, x, y, mSampledInputXs.back(),
- mSampledInputYs.back(), ProximityInfoUtils::getDistanceInt(
- x, y, mSampledInputXs.back(), mSampledInputYs.back()),
- mProximityInfo->getMostCommonKeyWidth()
- / LAST_POINT_SKIP_DISTANCE_SCALE);
- }
- return popped;
- }
- }
- }
-
- if (nodeCodePoint >= 0 && (x < 0 || y < 0)) {
- const int keyId = proximityInfo->getKeyIndexOf(nodeCodePoint);
- if (keyId >= 0) {
- x = proximityInfo->getKeyCenterXOfKeyIdG(keyId);
- y = proximityInfo->getKeyCenterYOfKeyIdG(keyId);
- }
- }
-
- // Pushing point information.
- if (size > 0) {
- sampledLengthCache->push_back(
- sampledLengthCache->back() + getDistanceInt(
- x, y, sampledInputXs->back(), sampledInputYs->back()));
- } else {
- sampledLengthCache->push_back(0);
- }
- sampledInputXs->push_back(x);
- sampledInputYs->push_back(y);
- sampledInputTimes->push_back(time);
- sampledInputIndice->push_back(inputIndex);
- if (DEBUG_GEO_FULL) {
- AKLOGI("pushTouchPoint: x = %03d, y = %03d, time = %d, index = %d, popped ? %01d",
- x, y, time, inputIndex, popped);
- }
- return popped;
- }
+ std::vector<int> *sampledInputIndice);
+ static float calculateBeelineSpeedRate(const int mostCommonKeyWidth, const float averageSpeed,
+ const int id, const int inputSize, const int *const xCoordinates,
+ const int *const yCoordinates, const int *times, const int sampledInputSize,
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const std::vector<int> *const inputIndice);
+ static float getPointAngle(
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int index);
+ static float getPointsAngle(
+ const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs,
+ const int index0, const int index1, const int index2);
+ static bool suppressCharProbabilities(const int mostCommonKeyWidth,
+ const int sampledInputSize, const std::vector<int> *const lengthCache,
+ const int index0, const int index1,
+ std::vector<hash_map_compat<int, float> > *charProbabilities);
+ static float calculateSquaredDistanceFromSweetSpotCenter(
+ const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int keyIndex,
+ const int inputIndex);
+ static float calculateNormalizedSquaredDistance(
+ const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
+ const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex);
};
} // namespace latinime
#endif // LATINIME_PROXIMITY_INFO_STATE_UTILS_H
diff --git a/native/jni/src/proximity_info_utils.h b/native/jni/src/proximity_info_utils.h
index 0930207..24917d8 100644
--- a/native/jni/src/proximity_info_utils.h
+++ b/native/jni/src/proximity_info_utils.h
@@ -61,7 +61,7 @@
const int primaryKey = inputCodes[i];
const int x = inputXCoordinates[i];
const int y = inputYCoordinates[i];
- int *proximities = &inputProximities[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL];
+ int *proximities = &inputProximities[i * MAX_PROXIMITY_CHARS_SIZE];
calculateProximities(keyXCoordinates, keyYCoordinates, keyWidths, keyHeights,
proximityCharsArray, maxProximityCharsSize, cellHeight, cellWidth, gridWidth,
mostCommonKeyWidth, keyCount, x, y, primaryKey, localeStr, codeToKeyMap,
@@ -71,9 +71,9 @@
if (DEBUG_PROXIMITY_CHARS) {
for (int i = 0; i < inputSize; ++i) {
AKLOGI("---");
- for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) {
+ for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; ++j) {
int proximityChar =
- inputProximities[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
+ inputProximities[i * MAX_PROXIMITY_CHARS_SIZE + j];
proximityChar += 0;
AKLOGI("--- (%d)%c", i, proximityChar);
}
diff --git a/native/jni/src/suggest_utils.h b/native/jni/src/suggest_utils.h
index 42cc5de..7d49cde 100644
--- a/native/jni/src/suggest_utils.h
+++ b/native/jni/src/suggest_utils.h
@@ -18,6 +18,7 @@
#define LATINIME_SUGGEST_UTILS_H
#include "defines.h"
+#include "proximity_info_params.h"
#include "proximity_info_state.h"
namespace latinime {
@@ -35,7 +36,7 @@
static const float R1 = NEUTRAL_SCORE_SQUARED_RADIUS;
static const float R2 = HALF_SCORE_SQUARED_RADIUS;
const float x = normalizedSquaredDistance / static_cast<float>(
- ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
+ ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
const float factor = max((x < R1)
? (A * (R1 - x) + B * x) / R1
: (B * (R2 - x) + C * (x - R1)) / (R2 - R1), MIN);
diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
index 015c271..4db5447 100644
--- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.keyboard;
import android.content.Context;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.view.inputmethod.InputMethodSubtype;
@@ -35,7 +34,6 @@
private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList();
private RichInputMethodManager mRichImm;
- private Resources mRes;
@Override
protected void setUp() throws Exception {
@@ -43,14 +41,13 @@
final Context context = getContext();
RichInputMethodManager.init(context);
mRichImm = RichInputMethodManager.getInstance();
- mRes = context.getResources();
SubtypeLocale.init(context);
}
public void testAllFullDisplayName() {
for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype, mRes);
- final String spacebarText = MainKeyboardView.getFullDisplayName(subtype, mRes);
+ final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype);
+ final String spacebarText = MainKeyboardView.getFullDisplayName(subtype);
final String languageName =
SubtypeLocale.getSubtypeLocaleDisplayName(subtype.getLocale());
if (SubtypeLocale.isNoLanguage(subtype)) {
@@ -63,7 +60,7 @@
public void testAllMiddleDisplayName() {
for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype, mRes);
+ final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype);
final String spacebarText = MainKeyboardView.getMiddleDisplayName(subtype);
if (SubtypeLocale.isNoLanguage(subtype)) {
assertEquals(subtypeName,
@@ -78,7 +75,7 @@
public void testAllShortDisplayName() {
for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype, mRes);
+ final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype);
final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
final String spacebarText = MainKeyboardView.getShortDisplayName(subtype);
final String languageCode = StringUtils.toTitleCase(locale.getLanguage(), locale);
@@ -120,17 +117,17 @@
SubtypeLocale.NO_LANGUAGE, "qwerty");
assertEquals("en_US", "English (US)",
- MainKeyboardView.getFullDisplayName(EN_US, mRes));
+ MainKeyboardView.getFullDisplayName(EN_US));
assertEquals("en_GB", "English (UK)",
- MainKeyboardView.getFullDisplayName(EN_GB, mRes));
+ MainKeyboardView.getFullDisplayName(EN_GB));
assertEquals("fr ", "Français",
- MainKeyboardView.getFullDisplayName(FR, mRes));
+ MainKeyboardView.getFullDisplayName(FR));
assertEquals("fr_CA", "Français (Canada)",
- MainKeyboardView.getFullDisplayName(FR_CA, mRes));
+ MainKeyboardView.getFullDisplayName(FR_CA));
assertEquals("de ", "Deutsch",
- MainKeyboardView.getFullDisplayName(DE, mRes));
+ MainKeyboardView.getFullDisplayName(DE));
assertEquals("zz ", "QWERTY",
- MainKeyboardView.getFullDisplayName(ZZ, mRes));
+ MainKeyboardView.getFullDisplayName(ZZ));
assertEquals("en_US", "English", MainKeyboardView.getMiddleDisplayName(EN_US));
assertEquals("en_GB", "English", MainKeyboardView.getMiddleDisplayName(EN_GB));
@@ -158,13 +155,13 @@
SubtypeLocale.NO_LANGUAGE, "azerty", null);
assertEquals("fr qwertz", "Français (QWERTZ)",
- MainKeyboardView.getFullDisplayName(FR_QWERTZ, mRes));
+ MainKeyboardView.getFullDisplayName(FR_QWERTZ));
assertEquals("de qwerty", "Deutsch (QWERTY)",
- MainKeyboardView.getFullDisplayName(DE_QWERTY, mRes));
+ MainKeyboardView.getFullDisplayName(DE_QWERTY));
assertEquals("en_US azerty", "English (US) (AZERTY)",
- MainKeyboardView.getFullDisplayName(US_AZERTY, mRes));
+ MainKeyboardView.getFullDisplayName(US_AZERTY));
assertEquals("zz azerty", "AZERTY",
- MainKeyboardView.getFullDisplayName(ZZ_AZERTY, mRes));
+ MainKeyboardView.getFullDisplayName(ZZ_AZERTY));
assertEquals("fr qwertz", "Français", MainKeyboardView.getMiddleDisplayName(FR_QWERTZ));
assertEquals("de qwerty", "Deutsch", MainKeyboardView.getMiddleDisplayName(DE_QWERTY));
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index 4e81de6..9af6dbc 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -33,6 +33,18 @@
private RichInputMethodManager mRichImm;
private Resources mRes;
+ InputMethodSubtype EN_US;
+ InputMethodSubtype EN_GB;
+ InputMethodSubtype ES_US;
+ InputMethodSubtype FR;
+ InputMethodSubtype FR_CA;
+ InputMethodSubtype DE;
+ InputMethodSubtype ZZ;
+ InputMethodSubtype DE_QWERTY;
+ InputMethodSubtype FR_QWERTZ;
+ InputMethodSubtype US_AZERTY;
+ InputMethodSubtype ZZ_AZERTY;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -41,11 +53,35 @@
mRichImm = RichInputMethodManager.getInstance();
mRes = context.getResources();
SubtypeLocale.init(context);
+
+ EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.US.toString(), "qwerty");
+ EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.UK.toString(), "qwerty");
+ ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ "es_US", "spanish");
+ FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.FRENCH.toString(), "azerty");
+ FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.CANADA_FRENCH.toString(), "qwerty");
+ DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ Locale.GERMAN.toString(), "qwertz");
+ ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ SubtypeLocale.NO_LANGUAGE, "qwerty");
+ DE_QWERTY = AdditionalSubtype.createAdditionalSubtype(
+ Locale.GERMAN.toString(), "qwerty", null);
+ FR_QWERTZ = AdditionalSubtype.createAdditionalSubtype(
+ Locale.FRENCH.toString(), "qwertz", null);
+ US_AZERTY = AdditionalSubtype.createAdditionalSubtype(
+ Locale.US.toString(), "azerty", null);
+ ZZ_AZERTY = AdditionalSubtype.createAdditionalSubtype(
+ SubtypeLocale.NO_LANGUAGE, "azerty", null);
+
}
public void testAllFullDisplayName() {
for (final InputMethodSubtype subtype : mSubtypesList) {
- final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype, mRes);
+ final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype);
if (SubtypeLocale.isNoLanguage(subtype)) {
final String noLanguage = mRes.getString(R.string.subtype_no_language);
assertTrue(subtypeName, subtypeName.contains(noLanguage));
@@ -74,21 +110,6 @@
// zz azerty T No language (AZERTY) in system locale
public void testPredefinedSubtypesInEnglish() {
- final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.US.toString(), "qwerty");
- final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.UK.toString(), "qwerty");
- final InputMethodSubtype ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- "es_US", "spanish");
- final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.FRENCH.toString(), "azerty");
- final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.CANADA_FRENCH.toString(), "qwerty");
- final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.GERMAN.toString(), "qwertz");
- final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocale.NO_LANGUAGE, "qwerty");
-
assertEquals("en_US", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_US));
assertEquals("en_GB", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_GB));
assertEquals("es_US", "spanish", SubtypeLocale.getKeyboardLayoutSetName(ES_US));
@@ -101,19 +122,19 @@
@Override
protected Void job(Resources res) {
assertEquals("en_US", "English (US)",
- SubtypeLocale.getSubtypeDisplayName(EN_US, res));
+ SubtypeLocale.getSubtypeDisplayName(EN_US));
assertEquals("en_GB", "English (UK)",
- SubtypeLocale.getSubtypeDisplayName(EN_GB, res));
+ SubtypeLocale.getSubtypeDisplayName(EN_GB));
assertEquals("es_US", "Español (EE.UU.)",
- SubtypeLocale.getSubtypeDisplayName(ES_US, res));
+ SubtypeLocale.getSubtypeDisplayName(ES_US));
assertEquals("fr ", "Français",
- SubtypeLocale.getSubtypeDisplayName(FR, res));
+ SubtypeLocale.getSubtypeDisplayName(FR));
assertEquals("fr_CA", "Français (Canada)",
- SubtypeLocale.getSubtypeDisplayName(FR_CA, res));
+ SubtypeLocale.getSubtypeDisplayName(FR_CA));
assertEquals("de ", "Deutsch",
- SubtypeLocale.getSubtypeDisplayName(DE, res));
+ SubtypeLocale.getSubtypeDisplayName(DE));
assertEquals("zz ", "No language (QWERTY)",
- SubtypeLocale.getSubtypeDisplayName(ZZ, res));
+ SubtypeLocale.getSubtypeDisplayName(ZZ));
return null;
}
};
@@ -121,26 +142,17 @@
}
public void testAdditionalSubtypesInEnglish() {
- final InputMethodSubtype DE_QWERTY = AdditionalSubtype.createAdditionalSubtype(
- Locale.GERMAN.toString(), "qwerty", null);
- final InputMethodSubtype FR_QWERTZ = AdditionalSubtype.createAdditionalSubtype(
- Locale.FRENCH.toString(), "qwertz", null);
- final InputMethodSubtype US_AZERTY = AdditionalSubtype.createAdditionalSubtype(
- Locale.US.toString(), "azerty", null);
- final InputMethodSubtype ZZ_AZERTY = AdditionalSubtype.createAdditionalSubtype(
- SubtypeLocale.NO_LANGUAGE, "azerty", null);
-
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@Override
protected Void job(Resources res) {
assertEquals("fr qwertz", "Français (QWERTZ)",
- SubtypeLocale.getSubtypeDisplayName(FR_QWERTZ, res));
+ SubtypeLocale.getSubtypeDisplayName(FR_QWERTZ));
assertEquals("de qwerty", "Deutsch (QWERTY)",
- SubtypeLocale.getSubtypeDisplayName(DE_QWERTY, res));
+ SubtypeLocale.getSubtypeDisplayName(DE_QWERTY));
assertEquals("en_US azerty", "English (US) (AZERTY)",
- SubtypeLocale.getSubtypeDisplayName(US_AZERTY, res));
+ SubtypeLocale.getSubtypeDisplayName(US_AZERTY));
assertEquals("zz azerty", "No language (AZERTY)",
- SubtypeLocale.getSubtypeDisplayName(ZZ_AZERTY, res));
+ SubtypeLocale.getSubtypeDisplayName(ZZ_AZERTY));
return null;
}
};
@@ -148,38 +160,23 @@
}
public void testPredefinedSubtypesInFrench() {
- final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.US.toString(), "qwerty");
- final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.UK.toString(), "qwerty");
- final InputMethodSubtype ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- "es_US", "spanish");
- final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.FRENCH.toString(), "azerty");
- final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.CANADA_FRENCH.toString(), "qwerty");
- final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.GERMAN.toString(), "qwertz");
- final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocale.NO_LANGUAGE, "qwerty");
-
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@Override
protected Void job(Resources res) {
assertEquals("en_US", "English (US)",
- SubtypeLocale.getSubtypeDisplayName(EN_US, res));
+ SubtypeLocale.getSubtypeDisplayName(EN_US));
assertEquals("en_GB", "English (UK)",
- SubtypeLocale.getSubtypeDisplayName(EN_GB, res));
+ SubtypeLocale.getSubtypeDisplayName(EN_GB));
assertEquals("es_US", "Español (EE.UU.)",
- SubtypeLocale.getSubtypeDisplayName(ES_US, res));
+ SubtypeLocale.getSubtypeDisplayName(ES_US));
assertEquals("fr ", "Français",
- SubtypeLocale.getSubtypeDisplayName(FR, res));
+ SubtypeLocale.getSubtypeDisplayName(FR));
assertEquals("fr_CA", "Français (Canada)",
- SubtypeLocale.getSubtypeDisplayName(FR_CA, res));
+ SubtypeLocale.getSubtypeDisplayName(FR_CA));
assertEquals("de ", "Deutsch",
- SubtypeLocale.getSubtypeDisplayName(DE, res));
+ SubtypeLocale.getSubtypeDisplayName(DE));
assertEquals("zz ", "Pas de langue (QWERTY)",
- SubtypeLocale.getSubtypeDisplayName(ZZ, res));
+ SubtypeLocale.getSubtypeDisplayName(ZZ));
return null;
}
};
@@ -187,26 +184,17 @@
}
public void testAdditionalSubtypesInFrench() {
- final InputMethodSubtype DE_QWERTY = AdditionalSubtype.createAdditionalSubtype(
- Locale.GERMAN.toString(), "qwerty", null);
- final InputMethodSubtype FR_QWERTZ = AdditionalSubtype.createAdditionalSubtype(
- Locale.FRENCH.toString(), "qwertz", null);
- final InputMethodSubtype US_AZERTY = AdditionalSubtype.createAdditionalSubtype(
- Locale.US.toString(), "azerty", null);
- final InputMethodSubtype ZZ_AZERTY = AdditionalSubtype.createAdditionalSubtype(
- SubtypeLocale.NO_LANGUAGE, "azerty", null);
-
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@Override
protected Void job(Resources res) {
assertEquals("fr qwertz", "Français (QWERTZ)",
- SubtypeLocale.getSubtypeDisplayName(FR_QWERTZ, res));
+ SubtypeLocale.getSubtypeDisplayName(FR_QWERTZ));
assertEquals("de qwerty", "Deutsch (QWERTY)",
- SubtypeLocale.getSubtypeDisplayName(DE_QWERTY, res));
+ SubtypeLocale.getSubtypeDisplayName(DE_QWERTY));
assertEquals("en_US azerty", "English (US) (AZERTY)",
- SubtypeLocale.getSubtypeDisplayName(US_AZERTY, res));
+ SubtypeLocale.getSubtypeDisplayName(US_AZERTY));
assertEquals("zz azerty", "Aucune langue (AZERTY)",
- SubtypeLocale.getSubtypeDisplayName(ZZ_AZERTY, res));
+ SubtypeLocale.getSubtypeDisplayName(ZZ_AZERTY));
return null;
}
};