diff options
Diffstat (limited to 'java')
144 files changed, 4677 insertions, 3918 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index 06d852bb0..6b823829f 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -1,3 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" coreApp="true" package="com.android.inputmethod.latin"> diff --git a/java/proguard.flags b/java/proguard.flags index 34e23aa9a..376a0e06c 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -3,10 +3,6 @@ <init>(...); } --keep class com.android.inputmethod.latin.Flag { - *; -} - -keep class com.android.inputmethod.keyboard.ProximityInfo { <init>(com.android.inputmethod.keyboard.ProximityInfo); } @@ -24,6 +20,10 @@ boolean equalsIgnoreCase(...); } +-keep class com.android.inputmethod.latin.InputPointers { + *; +} + -keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment { *; } @@ -41,13 +41,7 @@ } -keep class com.android.inputmethod.latin.ResearchLogger { - void setLogFileManager(...); - void clearAll(); - com.android.inputmethod.latin.ResearchLogger$LogFileManager getLogFileManager(); -} - --keep class com.android.inputmethod.latin.ResearchLogger$LogFileManager { - java.lang.String getContents(); + void flush(); } -keep class com.android.inputmethod.keyboard.KeyboardLayoutSet$Builder { diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict Binary files differindex 98a9361b5..e02e300e4 100644 --- a/java/res/raw/main_en.dict +++ b/java/res/raw/main_en.dict diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict Binary files differindex 717078c93..8e616591c 100644 --- a/java/res/raw/main_fr.dict +++ b/java/res/raw/main_fr.dict diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 7431fced8..30d5f5da1 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android-sleutelbord"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-sleutelbord (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android-sleutelbordinstellings"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-speltoetser"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-speltoetser (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Speltoetser se instellings"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Soek kontakname op"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Speltoetser gebruik inskrywings uit jou kontaklys"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreer met sleuteldruk"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Verstek"</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="enable_span_insert" msgid="7204653105667167620">"Aktiveer herkorrigerings"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Stel voorstelle vir herkorrigerings"</string> <string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Voeg woordeboeke by"</string> <string name="main_dictionary" msgid="4798763781818361168">"Hoofwoordeboek"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Matig"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressief"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Baie aggressief"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Volgendewoordvoorstelle"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gebruik vorige woord om voorstelle te verbeter"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Volgendewoordvoorspelling"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gebruik vorige woord ook vir voorspelling"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Stel volgende woord voor"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Gebaseer op vorige woord"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Gestoor"</string> <string name="label_go_key" msgid="1635148082137219148">"Gaan"</string> <string name="label_next_key" msgid="362972844525672568">"Volgende"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Steminvoer is gedeaktiveer"</string> <string name="configure_input_method" msgid="373356270290742459">"Stel invoermetodes op"</string> <string name="language_selection_title" msgid="1651299598555326750">"Invoertale"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"let op die tydstempel in die loglêer"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Aangetekende tydstempel"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Moenie hierdie sessie aanteken nie"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Sessie se loglêer uitgevee"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Sessie se loglêer uitgevee"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Sessie se loglêer NIE uitgevee nie"</string> <string name="select_language" msgid="3693815588777926848">"Invoertale"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Raak weer om te stoor"</string> <string name="has_dictionary" msgid="6071847973466625007">"Woordeboek beskikbaar"</string> diff --git a/java/res/values-am/strings-appname.xml b/java/res/values-am/strings-appname.xml new file mode 100644 index 000000000..da0ab2f16 --- /dev/null +++ b/java/res/values-am/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android የፊደል አራሚ"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Android የቁልፍ ሰሌዳ ቅንብሮች"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"የፊደል አራሚ ቅንብሮች"</string> +</resources> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index d70c05da9..69493b750 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"የAndroid ቁልፍሰሌዳ"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"የAndroid ቁልፍ ሰሌዳ (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"የAndroid ቁልፍሰሌዳ ቅንብሮች"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android የፊደል ማረሚያ"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android የፊደል ማረሚያ (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"የፊደል አራሚ ቅንብሮች"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"የእውቅያ ስሞችን ተመልከት"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ፊደል አራሚ ከእውቅያ ዝርዝርህ የገቡትን ይጠቀማል"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ነባሪ"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"የዕውቂያ ስም ጠቁም"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ከዕውቂያዎች ለጥቆማዎች እና ማስተካከያዎች ስሞች ተጠቀም"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"ድጋሚ ለማስተካከል አንቃ"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"ድጋሚ ለማስተካከል ጥቆማዎችን አዘጋጅ"</string> <string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"መዝገበ ቃላቶች ጨምር"</string> <string name="main_dictionary" msgid="4798763781818361168">"ዋና መዝገበ ቃላት"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"መጠነኛ"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"ኃይለኛ"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"በጣም ቁጡ"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"የቀጣይ ቃል አስተያየቶች"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"ምክሮችን ለማሻሻል ቀዳሚ ቃል ተጠቀም"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"የቀጣይ ቃል ግምት"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"ለትንበያ የቀደመ ቃል እንዲሁ ተጠቀም"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"የቀጣይ ቃል አስተያየቶች"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"በቀዳሚው ቃል ላይ የተመሠረተ"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ተቀምጧል"</string> <string name="label_go_key" msgid="1635148082137219148">"ሂድ"</string> <string name="label_next_key" msgid="362972844525672568">"በመቀጠል"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"የድምፅ ግቤት ቦዝኗል"</string> <string name="configure_input_method" msgid="373356270290742459">"ግቤት ሜተዶችን አዋቀር"</string> <string name="language_selection_title" msgid="1651299598555326750">"ቋንቋዎች አግቤት"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"የምዝግብ ማስታወሻ ጊዜ ማህተም ማስታወሻ"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"የጊዜ ማህተም ተመዝግቧል"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"ይህን ክፍለ ጊዜ እንዳትመዘግበው"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"የክፍለጊዜ ምዝግብ ማስታወሻ በመሰረዝ ላይ"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"የክፍለ ጊዜ ምዝግብ ማስታወሻ ተሰርዟል"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"የክፍለጊዜ ምዝግብ ማስታወሻ አልተሰረዘም"</string> <string name="select_language" msgid="3693815588777926848">"ቋንቋዎች አግቤት"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"ለማስቀመጥ እንደገና ንካ"</string> <string name="has_dictionary" msgid="6071847973466625007">"መዝገበ ቃላት አለ"</string> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 5678c40c8..eb7e30604 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"لوحة مفاتيح Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"لوحة مفاتيح Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"إعدادات لوحة مفاتيح Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"التدقيق الإملائي في Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"التدقيق الإملائي في Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"إعدادات التدقيق الإملائي"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"بحث في أسماء جهات الاتصال"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"يستخدم المدقق الإملائي إدخالات من قائمة جهات الاتصال"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"اهتزاز عند ضغط مفتاح"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"افتراضي"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"اقتراح أسماء جهات الاتصال"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"استخدام الأسماء من جهات الاتصال للاقتراحات والتصحيحات"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"تمكين عمليات إعادة التصحيح"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"تعيين اقتراحات لعمليات إعادة التصحيح"</string> <string name="auto_cap" msgid="1719746674854628252">"أحرف كبيرة تلقائيًا"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"القواميس الإضافية"</string> <string name="main_dictionary" msgid="4798763781818361168">"القاموس الرئيسي"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"معتدل"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"حاد"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"شديد الصرامة"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"اقتراحات الكلمات التالية"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"استخدام الكلمة السابقة لتحسين الاقتراحات"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"تنبؤ الكلمات التالية"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"استخدام الكلمة السابقة أيضًا للتنبؤ"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"اقتراحات الكلمات التالية"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"استنادًا إلى الكلمة السابقة"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : تم الحفظ"</string> <string name="label_go_key" msgid="1635148082137219148">"تنفيذ"</string> <string name="label_next_key" msgid="362972844525672568">"التالي"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"الإدخال الصوتي مُعطل"</string> <string name="configure_input_method" msgid="373356270290742459">"تهيئة طرق الإدخال"</string> <string name="language_selection_title" msgid="1651299598555326750">"لغات الإدخال"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"ملاحظة طابع زمني في سجل"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"تم تسجيل الطابع الزمني"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"عدم تسجيل هذه الجلسة"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"جارٍ حذف سجل الجلسة"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"تم حذف سجل الجلسة"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"لم يتم حذف سجل الجلسة"</string> <string name="select_language" msgid="3693815588777926848">"لغات الإدخال"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"المس مرة أخرى للحفظ"</string> <string name="has_dictionary" msgid="6071847973466625007">"القاموس متاح"</string> diff --git a/java/res/values-be/strings-appname.xml b/java/res/values-be/strings-appname.xml new file mode 100644 index 000000000..226de7cbd --- /dev/null +++ b/java/res/values-be/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Iнструмент праверкi правапiсу для Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Налады клавіятуры Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Налады праверкі арфаграфіі"</string> +</resources> diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml index 4d14565b9..d67ddc034 100644 --- a/java/res/values-be/strings.xml +++ b/java/res/values-be/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Клавіятура Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавіятура Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Налады клавіятуры Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ўводу"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Iнструмент праверкi правапiсу для Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Iнструмент праверкi правапiсу для Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Налады праверкі арфаграфіі"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукаць імёны кантактаў"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Модуль праверкі правапісу выкарыстоўвае запісы са спісу кантактаў"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібрацыя пры націску клавіш"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Прапан. імёны кантактаў"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Выкарыстоўваць імёны са спісу кантактаў для прапаноў і выпраўл."</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Уключыць карэкцiроўкі"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаць прапановы для карэкцiроўкі"</string> <string name="auto_cap" msgid="1719746674854628252">"Аўтаматычна рабіць вялікія літары"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Дадатковыя слоўнікі"</string> <string name="main_dictionary" msgid="4798763781818361168">"Асноўны слоўнік"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Сціплы"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агрэсіўны"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Вельмі агрэсіўны"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Падказкi для наступнага слова"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Выкарыстаць папярэдняе слова, каб палепшыць прапановы"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Падказка наступнага слова"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Выкарыстанне папярэдняга слова для падказак"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Падказкi для наступнага слова"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"На аснове папярэдняга слова"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Захаваныя"</string> <string name="label_go_key" msgid="1635148082137219148">"Пачаць"</string> <string name="label_next_key" msgid="362972844525672568">"Далей"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Галасавы набор адкл."</string> <string name="configure_input_method" msgid="373356270290742459">"Налада метадаў уводу"</string> <string name="language_selection_title" msgid="1651299598555326750">"Мовы ўводу"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Пазначыць час у гiсторыi"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Запiсаныя пазнакі"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Не рэгістраваць гэты сеанс"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Выдаленне гiсторыi сеанса"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Гiсторыя сеанса выдалена"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Гiсторыя сеанса НЕ выдалена"</string> <string name="select_language" msgid="3693815588777926848">"Мовы ўводу"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Дакраніцеся зноў, каб захаваць"</string> <string name="has_dictionary" msgid="6071847973466625007">"Слоўнік даступны"</string> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 106a91806..5cd6fcaa7 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура на Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавиатура на Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Настройки на клавиатурата на Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Програма за правописна проверка за Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Програма за правописна проверка за Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройки за проверка на правописа"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Търсене на имена"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"За проверка на правописа се ползват записи от списъка с контакти"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Да вибрира при натискане на клавиш"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По подразбиране"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Предложения за контакти"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Използване на имена от „Контакти“ за предложения и поправки"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Повторни поправки: Актив."</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаване на предложения за повторни поправки"</string> <string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Добавени речници"</string> <string name="main_dictionary" msgid="4798763781818361168">"Основен речник"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умерено"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агресивно"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Много агресивно"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Предложения за следващата дума"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Използване на предишната дума за подобряване на предложенията"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Предвиждане на следващата дума"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Използване на предишната дума и за предвиждане"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Предложения за следващата дума"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Въз основа на предишната дума"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Запазено"</string> <string name="label_go_key" msgid="1635148082137219148">"Старт"</string> <string name="label_next_key" msgid="362972844525672568">"Напред"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Глас. въвежд. е деакт."</string> <string name="configure_input_method" msgid="373356270290742459">"Конфигуриране на въвеждането"</string> <string name="language_selection_title" msgid="1651299598555326750">"Входни езици"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Отбелязване на часа в рег. файл"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Часът е записан"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Без регистр. на сесията"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Рег. файл на сесията се изтрива"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Рег. файл на сесията е изтрит"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Рег. файл на сесията НЕ Е изтрит"</string> <string name="select_language" msgid="3693815588777926848">"Езици за въвеждане"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Докоснете отново, за да запазите"</string> <string name="has_dictionary" msgid="6071847973466625007">"Има достъп до речник"</string> diff --git a/java/res/values-ca/strings-appname.xml b/java/res/values-ca/strings-appname.xml new file mode 100644 index 000000000..6a23c242b --- /dev/null +++ b/java/res/values-ca/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortogràfic d\'Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Configuració del teclat d\'Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configuració de la correcció ortogràfica"</string> +</resources> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index e3adbcff4..01cc422c4 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Teclat Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclat d\'Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Configuració del teclat d\'Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortogràfic d\'Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector ortogràfic d\'Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configuració de la correcció ortogràfica"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca noms de contactes"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortogràfic utilitza entrades de la llista de cont."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra en prémer tecles"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms contactes"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de Contactes per a suggeriments i correccions"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Activa la capacitat de tornar a corregir"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Estableix suggeriments per tornar a corregir"</string> <string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionaris complementaris"</string> <string name="main_dictionary" msgid="4798763781818361168">"Diccionari principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Molt agressiu"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Suggeriments de paraula següent"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilitza la paraula anterior per millorar els suggeriments"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Predicció de paraula següent"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilitza també la paraula anterior per a la predicció"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Suggeriments de paraula següent"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"En funció de la paraula anterior"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: desada"</string> <string name="label_go_key" msgid="1635148082137219148">"Vés"</string> <string name="label_next_key" msgid="362972844525672568">"Següent"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. veu desactiv."</string> <string name="configure_input_method" msgid="373356270290742459">"Configura mètodes d\'entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomes d\'entrada"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Indica m. horària al reg."</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Marca horària enregistrada"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"No enregistris la sessió"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Suprimint registre de ses."</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Registre de ses. suprimit"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Registre de ses. NO sup."</string> <string name="select_language" msgid="3693815588777926848">"Idiomes d\'entrada"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Torna a tocar per desar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Diccionari disponible"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 90d840cd8..9764ce501 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Klávesnice Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klávesnice Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kontrola pravopisu Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavení kontroly pravopisu"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhledat kontakty"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používá záznamy z vašeho seznamu kontaktů."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</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="enable_span_insert" msgid="7204653105667167620">"Povolit opětovné opravy"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavit návrhy pro opětovné opravy"</string> <string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplňkové slovníky"</string> <string name="main_dictionary" msgid="4798763781818361168">"Hlavní slovník"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mírné"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivní"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Velmi agresivní"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Návrh dalšího slova"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Použít předchozí slovo ke zlepšení návrhů"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Odhad dalšího slova"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použít předchozí slovo také pro odhad"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Návrhy dalšího slova"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na základě předchozího slova"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Uloženo"</string> <string name="label_go_key" msgid="1635148082137219148">"Přejít"</string> <string name="label_next_key" msgid="362972844525672568">"Další"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup vypnut"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurace metod zadávání"</string> <string name="language_selection_title" msgid="1651299598555326750">"Vstupní jazyky"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Uložit čas do protokolu"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Časové razítko vloženo"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Neprotokolovat relaci"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Mazání protokolu relace"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Protokol relace smazán"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Protokol relace nesmazán"</string> <string name="select_language" msgid="3693815588777926848">"Vstupní jazyky"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Opětovným dotykem provedete uložení"</string> <string name="has_dictionary" msgid="6071847973466625007">"Slovník k dispozici"</string> diff --git a/java/res/values-da/strings-appname.xml b/java/res/values-da/strings-appname.xml new file mode 100644 index 000000000..610d88aaa --- /dev/null +++ b/java/res/values-da/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-stavekontrol"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Indstillinger for Android-tastatur"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Indstillinger for stavekontrol"</string> +</resources> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 50b0b0a9f..e669f7168 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android-tastatur"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-tastatur (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android-tastatur-indstillinger"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontrol"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Forskningslogkommandoer"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-stavekontrol (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Indstillinger for stavekontrol"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå kontaktnavne op"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruger poster fra listen over kontaktpersoner"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</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="enable_span_insert" msgid="7204653105667167620">"Aktivér fornyet rettelse"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angiv forslag til fornyet rettelse"</string> <string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tillægsordbøger"</string> <string name="main_dictionary" msgid="4798763781818361168">"Hovedordbog"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Meget aggressiv"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Forslag til næste ord"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Brug forrige ord til at forbedre forslag"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Forudsigelse af næste ord"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Brug også tidligere ord til forudsigelse"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Forslag til næste ord"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Baseret på tidligere ord"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Gemt"</string> <string name="label_go_key" msgid="1635148082137219148">"Gå"</string> <string name="label_next_key" msgid="362972844525672568">"Næste"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Stemmeinput deaktiveret"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inputmetoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inputsprog"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Notér tidsstempel i log"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Noteret tidsstempel"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Logfør ikke denne session"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Sletter sessionslogfil"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Sessionslogfil slettet"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Sessionslog IKKE slettet"</string> <string name="select_language" msgid="3693815588777926848">"Inputsprog"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Tryk igen for at gemme"</string> <string name="has_dictionary" msgid="6071847973466625007">"Ordbog er tilgængelig"</string> diff --git a/java/res/values-de/strings-appname.xml b/java/res/values-de/strings-appname.xml new file mode 100644 index 000000000..bc77f722e --- /dev/null +++ b/java/res/values-de/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-Rechtschreibprüfung"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Android-Tastatureinstellungen"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Einstellungen für Rechtschreibprüfung"</string> +</resources> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 216aac5d9..827ff602b 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android-Tastatur"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-Tastatur (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatureinstellungen"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-Rechtschreibprüfung"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-Rechtschreibprüfung (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Einstellungen für Rechtschreibprüfung"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung verwendet Einträge aus Ihrer Kontaktliste."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Bei Tastendruck vibrieren"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</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="enable_span_insert" msgid="7204653105667167620">"Korrekturen aktivieren"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Vorschläge für Korrekturen festlegen"</string> <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Erweiterte Wörterbücher"</string> <string name="main_dictionary" msgid="4798763781818361168">"Allgemeines Wörterbuch"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mäßig"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Stark"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sehr stark"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Vorschläge für nächstes Wort"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Zur Verbesserung von Vorschlägen vorheriges Wort verwenden"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Vervollständigung für nächstes Wort"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Vorheriges Wort auch für Vervollständigung verwenden"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Vorschläge für nächstes Wort"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Auf Grundlage des vorherigen Wortes"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: gespeichert"</string> <string name="label_go_key" msgid="1635148082137219148">"Los"</string> <string name="label_next_key" msgid="362972844525672568">"Weiter"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spracheingabe deaktiviert"</string> <string name="configure_input_method" msgid="373356270290742459">"Eingabemethoden konfigurieren"</string> <string name="language_selection_title" msgid="1651299598555326750">"Eingabesprachen"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Zeitstempel im Protokoll"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Zeitstempel aufgenommen"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Nicht protokollieren"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Protokoll wird gelöscht..."</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Protokoll gelöscht"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Protokoll NICHT gelöscht"</string> <string name="select_language" msgid="3693815588777926848">"Eingabesprachen"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Zum Speichern erneut berühren"</string> <string name="has_dictionary" msgid="6071847973466625007">"Wörterbuch verfügbar"</string> diff --git a/java/res/values-el/strings-appname.xml b/java/res/values-el/strings-appname.xml new file mode 100644 index 000000000..6daf2d97c --- /dev/null +++ b/java/res/values-el/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Ορθογραφικός έλεγχος Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Ρυθμίσεις πληκτρολογίου Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ρυθμίσεις ορθογραφικού ελέγχου"</string> +</resources> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 486346a09..563e18124 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Πληκτρολόγιο Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Πληκτρολόγιο Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Ρυθμίσεις πληκτρολογίου Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Ορθογραφικός έλεγχος Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Ορθογραφικός έλεγχος Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ρυθμίσεις ορθογραφικού ελέγχου"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Αναζήτηση ονομάτων επαφών"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Ο ορθογρ. έλεγχος χρησιμοπ. καταχωρίσεις από τη λίστα επαφών σας"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Προεπιλογή"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Πρόταση ονομάτων επαφών"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Χρησιμοποιήστε ονόματα από τις Επαφές για προτάσεις και διορθ."</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Ενεργ. επανάλ. διορθώσεων"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ορισμός προτάσεων για επαναλήψεις διορθώσεων"</string> <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Πρόσθετα λεξικά"</string> <string name="main_dictionary" msgid="4798763781818361168">"Κύριο λεξικό"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Μέτρια"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Υψηλή"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Πολύ επιθετική"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Προτάσεις επόμενων λέξεων"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Χρήση προηγούμενης λέξης για τη βελτίωση προτάσεων"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Πρόβλεψη επόμενης λέξης"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Χρησιμοποιήστε, επίσης, την προηγούμενη λέξη για πρόβλεψη"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Προτάσεις επόμενων λέξεων"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Βάσει προηγούμενης λέξης"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string> <string name="label_go_key" msgid="1635148082137219148">"Μετ."</string> <string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Απεν. φωνητ. είσοδος"</string> <string name="configure_input_method" msgid="373356270290742459">"Διαμόρφωση μεθόδων εισαγωγής"</string> <string name="language_selection_title" msgid="1651299598555326750">"Γλώσσες εισόδου"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Χρόνος στο αρχείο καταγρ."</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Καταγεγραμμένος χρόνος"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Χωρίς αρχείο καταγραφής"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Διαγραφή αρχείου σύνδεσης"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Αρχείο καταγρ. διαγράφηκε"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Αρχείο καταγρ. ΔΕΝ διαγρ."</string> <string name="select_language" msgid="3693815588777926848">"Γλώσσες εισόδου"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Αγγίξτε ξανά για αποθήκευση"</string> <string name="has_dictionary" msgid="6071847973466625007">"Λεξικό διαθέσιμο"</string> diff --git a/java/res/values-en-rGB/strings-appname.xml b/java/res/values-en-rGB/strings-appname.xml new file mode 100644 index 000000000..53f9e9d0e --- /dev/null +++ b/java/res/values-en-rGB/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android spell checker"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Android keyboard settings"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Spell checking settings"</string> +</resources> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 502007666..513fae4c5 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android keyboard settings"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android spell checker"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android spell checker (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Spellchecking settings"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Spell checker uses entries from your contact list"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on key-press"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</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="enable_span_insert" msgid="7204653105667167620">"Enable recorrections"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Set suggestions for recorrections"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Add-on dictionaries"</string> <string name="main_dictionary" msgid="4798763781818361168">"Main dictionary"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressive"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Very aggressive"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Next word suggestions"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Use previous word to improve suggestion"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Next word prediction"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use previous word also for prediction"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Next word suggestions"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Based on previous word"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saved"</string> <string name="label_go_key" msgid="1635148082137219148">"Go"</string> <string name="label_next_key" msgid="362972844525672568">"Next"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Voice input is disabled"</string> <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string> <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Note timestamp in log"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Recorded timestamp"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Do not log this session"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Deleting session log"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Session log deleted"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Session log NOT deleted"</string> <string name="select_language" msgid="3693815588777926848">"Input languages"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Touch again to save"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dictionary available"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 34ad0a420..3eb32c12a 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado de Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortográfico de Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector ortográfico de Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configuración del corrector ortográfico"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nombres contactos"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortográfico usa entradas de tu lista de contactos."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</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="enable_span_insert" msgid="7204653105667167620">"Activar correcciones"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para realizar correcciones"</string> <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string> <string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresivo"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sugerencias para la palabra siguiente"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar la palabra anterior para mejorar las sugerencias"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Predicción de la palabra siguiente"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usar la palabra anterior también para predicción."</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sugerencias de palabra siguiente"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Según la palabra anterior"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string> <string name="label_go_key" msgid="1635148082137219148">"Ir"</string> <string name="label_next_key" msgid="362972844525672568">"Siguiente"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"La entrada por voz está inhabilitada"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Marcar tiempo en registro"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Marca tiempo registrada"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"No registrar esta sesión"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Eliminando registro"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Registro sesión eliminado"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"NO se eliminó el registro"</string> <string name="select_language" msgid="3693815588777926848">"Idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Vuelve a tocar para guardar."</string> <string name="has_dictionary" msgid="6071847973466625007">"Diccionario disponible"</string> diff --git a/java/res/values-es/strings-appname.xml b/java/res/values-es/strings-appname.xml new file mode 100644 index 000000000..e716e7d89 --- /dev/null +++ b/java/res/values-es/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortográfico de Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Ajustes del teclado de Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ajustes del corrector ortográfico"</string> +</resources> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 3c60aa6e2..1eee5d970 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Ajustes del teclado de Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones entrada texto"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector de Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector de Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ajustes del corrector ortográfico"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Nombres de contactos"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Añadir nombres de tu lista de contactos al corrector"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</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="enable_span_insert" msgid="7204653105667167620">"Activar nuevas correcciones"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para nuevas correcciones"</string> <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string> <string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Parcial"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresiva"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sugerir siguiente palabra"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar palabra anterior para mejorar las sugerencias"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Predecir siguiente palabra"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar también la palabra anterior para realizar la predicción"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sugerir siguiente palabra"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Según la palabra anterior"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string> <string name="label_go_key" msgid="1635148082137219148">"Ir"</string> <string name="label_next_key" msgid="362972844525672568">"Sig."</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entrada de voz inhabilitada"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Anotar marca tiempo en registro"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Marca de tiempo registrada"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"No registrar esta sesión"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Eliminando registro..."</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Registro eliminado"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Registro no eliminado"</string> <string name="select_language" msgid="3693815588777926848">"Idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Toca otra vez para guardar."</string> <string name="has_dictionary" msgid="6071847973466625007">"Hay un diccionario disponible"</string> diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml index 4a592c36b..a68e5656f 100644 --- a/java/res/values-et/strings.xml +++ b/java/res/values-et/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Androidi klaviatuur"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-klaviatuur (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Androidi klaviatuuriseaded"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Sisestusvalikud"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidi õigekirjakontroll"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidi õigekirjakontroll (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Õigekirjakontrolli seaded"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakti nimede kontroll."</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Õigekirjakontroll kasutab teie kontaktisikute loendi sissekandeid"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreeri klahvivajutusel"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</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="enable_span_insert" msgid="7204653105667167620">"Uute paranduste lubamine"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Soovituste seadmine uute paranduste jaoks"</string> <string name="auto_cap" msgid="1719746674854628252">"Automaatne suurtähtede kasutamine"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Pistiksõnaraamatud"</string> <string name="main_dictionary" msgid="4798763781818361168">"Peamine sõnaraamat"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mõõdukas"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiivne"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Väga agressiivne"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Järgmise sõna soovitused"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Kasuta soovituste täiustamiseks eelmist sõna"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Järgmise sõna ennustus"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Kasuta ennustuseks ka eelmist sõna"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Järgmise sõna soovitused"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Eelmise sõna põhjal"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : salvestatud"</string> <string name="label_go_key" msgid="1635148082137219148">"Mine"</string> <string name="label_next_key" msgid="362972844525672568">"Edasi"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Kõnesisend on keelatud"</string> <string name="configure_input_method" msgid="373356270290742459">"Sisestusmeetodite seadistamine"</string> <string name="language_selection_title" msgid="1651299598555326750">"Sisestuskeeled"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Märgi ajatempel logisse"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Salvestatud ajatemplid"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Ära logi seda seanssi"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Seansi logi kustutamine"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Seansi logi kustutatud"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Seansi logi EI kustutatud"</string> <string name="select_language" msgid="3693815588777926848">"Sisestuskeeled"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Salvestamiseks puudutage uuesti"</string> <string name="has_dictionary" msgid="6071847973466625007">"Sõnastik saadaval"</string> diff --git a/java/res/values-fa/strings-appname.xml b/java/res/values-fa/strings-appname.xml new file mode 100644 index 000000000..263f166f8 --- /dev/null +++ b/java/res/values-fa/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"غلطگیر املای Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"تنظیمات صفحه کلید Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"تنظیمات غلط گیر املا"</string> +</resources> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index bc0e85b77..d61d7eb62 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"صفحه کلید Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"صفحه کلید (Android (AOSP"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"تنظیمات صفحه کلید Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه های ورودی"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"غلطگیر املای Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"فرمانهای گزارشگیری پژوهش"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"غلطگیر املای Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"تنظیمات غلط گیری املایی"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"جستجوی نام مخاطبین"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"غلطگیر املا از ورودیهای لیست مخاطبین شما استفاده میکند"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"لرزش با فشار کلید"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش فرض"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نام های مخاطب"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"برای پیشنهاد و تصحیح از نام مخاطبین استفاده شود"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"فعال کردن تصحیح مجدد"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"تنظیم پیشنهادات برای تصحیح مجدد"</string> <string name="auto_cap" msgid="1719746674854628252">"نوشتن با حروف بزرگ خودکار"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"فرهنگهای لغت افزودنی"</string> <string name="main_dictionary" msgid="4798763781818361168">"فرهنگ لغت اصلی"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"متوسط"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"فعال"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"بسیار پرخاشگرانه"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"پیشنهادات کلمه بعدی"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"برای بهبود پیشنهاد از کلمه قبلی استفاده شود"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"پیشبینی کلمه بعدی"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"استفاده از کلمه قبلی برای پیش بینی"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"پیشنهادات کلمه بعدی"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"بر اساس کلمه قبلی"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ذخیره شد"</string> <string name="label_go_key" msgid="1635148082137219148">"برو"</string> <string name="label_next_key" msgid="362972844525672568">"بعدی"</string> @@ -115,6 +108,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ورودی صدا غیرفعال است"</string> <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روش های ورودی"</string> <string name="language_selection_title" msgid="1651299598555326750">"زبان های ورودی"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"یادداشت مهر زمان در گزارش"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"مهر زمان ثبت شده"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"از این جلسه گزارشگیری نشود"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"در حال حذف گزارش جلسه"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"گزارش جلسه حذف شد"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"گزارش جلسه حذف نشد"</string> <string name="select_language" msgid="3693815588777926848">"زبانهای ورودی"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"برای ذخیره دوباره لمس کنید"</string> <string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string> diff --git a/java/res/values-fi/strings-appname.xml b/java/res/values-fi/strings-appname.xml new file mode 100644 index 000000000..fefb92e15 --- /dev/null +++ b/java/res/values-fi/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-oikoluku"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Android-näppäimistön asetukset"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Oikolukuasetukset"</string> +</resources> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 97002edfe..c036142a6 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android-näppäimistö"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-näppäimistö (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android-näppäimistön asetukset"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-oikoluku"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-oikoluku (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Oikoluvun asetukset"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Hae kontaktien nimiä"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Oikeinkirjoituksen tarkistus käyttää kontaktiluettelosi tietoja."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Käytä värinää näppäimiä painettaessa"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</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="enable_span_insert" msgid="7204653105667167620">"Ota korjaukset käyttöön"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Aseta korjausehdotuksia"</string> <string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Lisäsanakirjat"</string> <string name="main_dictionary" msgid="4798763781818361168">"Pääsanakirja"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Osittainen"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Täysi"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Hyvin aggressiivinen"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Seuraavan sanan ehdotukset"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Paranna ehdotuksia aiempien sanojen avulla"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Seuraavan sanan ennakointi"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Käytä edellistä sanaa myös ennakointiin"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Seuraavan sanan ehdotukset"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Perustuu edelliseen sanan"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Tallennettu"</string> <string name="label_go_key" msgid="1635148082137219148">"Siirry"</string> <string name="label_next_key" msgid="362972844525672568">"Seur."</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Ääniohjaus on pois käytöstä"</string> <string name="configure_input_method" msgid="373356270290742459">"Määritä syöttötavat"</string> <string name="language_selection_title" msgid="1651299598555326750">"Syöttökielet"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Merkitse aikaleima lokiin"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Merkitty aikaleima"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Älä tallenna tätä käyttök."</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Poistetaan lokia"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Käyttökertaloki poistettu"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Lokia EI poistettu"</string> <string name="select_language" msgid="3693815588777926848">"Syöttökielet"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Tallenna koskettamalla uudelleen"</string> <string name="has_dictionary" msgid="6071847973466625007">"Sanakirja saatavilla"</string> diff --git a/java/res/values-fr/strings-appname.xml b/java/res/values-fr/strings-appname.xml new file mode 100644 index 000000000..da7671b1f --- /dev/null +++ b/java/res/values-fr/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Correcteur orthographique Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Paramètres du clavier Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Paramètres du correcteur orthographique"</string> +</resources> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 285d2226c..b5b1eac64 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Clavier Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Correcteur orthographique Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Correcteur orthographique Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Paramètre du correcteur orthographique"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Correcteur orthographique utilise entrées de liste de contacts."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</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="enable_span_insert" msgid="7204653105667167620">"Activer la recorrection"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Définir des suggestions de recorrection"</string> <string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dictionnaires complémentaires"</string> <string name="main_dictionary" msgid="4798763781818361168">"Dictionnaire principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Simple"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Proactive"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Très exigeante"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Suggestions pour le mot suivant"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Améliorer les suggestions grâce au mot précédent"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Prédiction du mot suivant"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utiliser le mot précédent pour la prédiction"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Suggestions pour le mot suivant"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Suggestions basées sur le mot précédent"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Suiv."</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Saisie vocale désactivée"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurer les modes de saisie"</string> <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Noter heure dans journal"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Heure enregistrée."</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Ne pas enregistrer session"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Suppr. journal session…"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Journal session supprimé."</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Journal session PAS suppr."</string> <string name="select_language" msgid="3693815588777926848">"Langues de saisie"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Appuyer de nouveau pour enregistrer"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string> diff --git a/java/res/values-hi/strings-appname.xml b/java/res/values-hi/strings-appname.xml new file mode 100644 index 000000000..c8d34ec3c --- /dev/null +++ b/java/res/values-hi/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android वर्तनी परीक्षक"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Android कीबोर्ड सेटिंग"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"वर्तनी जांच सेटिंग"</string> +</resources> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 2b1808460..a567faf57 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android कीबोर्ड"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android कीबोर्ड (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android कीबोर्ड सेटिंग"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्प"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android वर्तनी परीक्षक"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android वर्तनी परीक्षक (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"वर्तनी जांच सेटिंग"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"संपर्क नामों को खोजें"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"वर्तनी परीक्षक आपकी संपर्क सूची की प्रविष्टियों का उपयोग करता है"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"कुंजी दबाने पर कंपन करता है"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"डिफ़ॉल्ट"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"संपर्क नाम सुझाएं"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव और सुधार के लिए संपर्क से नामों का उपयोग करें"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"पुन: सुधार सक्षम करें"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"पुन: सुधार के लिए सुझाव सेट करें"</string> <string name="auto_cap" msgid="1719746674854628252">"स्वत: अक्षर बड़े करना"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-ऑन डिक्शनरी"</string> <string name="main_dictionary" msgid="4798763781818361168">"मुख्य डिक्शनरी"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"साधारण"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"तीव्र"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"बहुत तीव्र"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"अगला शब्द सुझाव"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"सुझावों को बेहतर बनाने के लिए पिछले शब्द का उपयोग करें"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"अगला शब्द पूर्वानुमान"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"पूर्वानुमान के लिए पिछले शब्द का उपयोग करें"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"अगले शब्द सुझाव"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"पिछले शब्द के आधार पर"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: सहेजा गया"</string> <string name="label_go_key" msgid="1635148082137219148">"जाएं"</string> <string name="label_next_key" msgid="362972844525672568">"अगला"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ध्वनि इनपुट अक्षम है"</string> <string name="configure_input_method" msgid="373356270290742459">"इनपुट पद्धति कॉन्फ़िगर करें"</string> <string name="language_selection_title" msgid="1651299598555326750">"इनपुट भाषा"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"लॉग में टाइमस्टैम्प नोट करें"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"रिकॉर्ड किया गया टाइमस्टैम्प"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"इस सत्र को लॉग न करें"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"सत्र लॉग हटाया जा रहा है"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"सत्र लॉग हटाया गया"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"सत्र लॉग हटाया नहीं गया"</string> <string name="select_language" msgid="3693815588777926848">"इनपुट भाषाएं"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"सहेजने के लिए पुन: स्पर्श करें"</string> <string name="has_dictionary" msgid="6071847973466625007">"शब्दकोश उपलब्ध है"</string> diff --git a/java/res/values-hr/strings-appname.xml b/java/res/values-hr/strings-appname.xml new file mode 100644 index 000000000..6457a21cf --- /dev/null +++ b/java/res/values-hr/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidova provjera pravopisa"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Postavke Androidove tipkovnice"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Postavke provjere pravopisa"</string> +</resources> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index a59d4698f..76f6edfa8 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android tipkovnica"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android tipkovnica (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Postavke tipkovnice za Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidova provjera pravopisa"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidova provjera pravopisa (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Postavke provjere pravopisa"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Potražite imena kontakata"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Provjera pravopisa upotrebljava unose iz vašeg popisa kontakata"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibracija pri pritisku na tipku"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</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="enable_span_insert" msgid="7204653105667167620">"Omogući ponovne ispravke"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Postavite prijedloge za ponovne ispravke"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Rječnici-dodaci"</string> <string name="main_dictionary" msgid="4798763781818361168">"Glavni rječnik"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Skromno"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivno"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Vrlo agresivno"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Prijedlozi za sljedeću riječ"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Upotrijebi prethodnu riječ radi poboljšanja prijedloga"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Predviđanje sljedeće riječi"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Upotrijebite prethodnu riječ i za predviđanje"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Prijedlozi za sljedeću riječ"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na temelju prethodne riječi"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Spremljeno"</string> <string name="label_go_key" msgid="1635148082137219148">"Idi"</string> <string name="label_next_key" msgid="362972844525672568">"Dalje"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. unos onemog."</string> <string name="configure_input_method" msgid="373356270290742459">"Konfiguriraj načine ulaza"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jezici unosa"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Zabilježi razdoblje u dnevniku"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Zabilježeno razdoblje"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Ne bilježi ovu sesiju"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Brisanje dnevnika sesije"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Izbrisan dnevnik sesije"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Dnevnik sesije NIJE izbrisan"</string> <string name="select_language" msgid="3693815588777926848">"Jezici unosa"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Dodirnite ponovo za spremanje"</string> <string name="has_dictionary" msgid="6071847973466625007">"Rječnik je dostupan"</string> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 0eac1a95b..4bf9f4280 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android-billentyűzet"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-billentyűzet (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android billentyűzetbeállítások"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidos helyesírás-ellenőrző"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidos helyesírás-ellenőrző (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Helyesírás-ellenőrzés beállításai"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Névjegyek keresése"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"A helyesírás-ellenőrző használja a névjegyek bejegyzéseit"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rezgés billentyű megnyomása esetén"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</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="enable_span_insert" msgid="7204653105667167620">"Újbóli javítás engedélyezése"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Javaslatok beállítása az újbóli javításokhoz"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Bővítmények: szótárak"</string> <string name="main_dictionary" msgid="4798763781818361168">"Fő szótár"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mérsékelt"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresszív"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Nagyon agresszív"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Következő szóra vonatkozó javaslatok"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Javaslatok fejlesztése az előző szó használatával"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Következő szó előrejelzése"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Az előző szó használata a prediktív bevitelhez is"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Következő szóra vonatkozó javaslatok"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Az előző szó alapján"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : mentve"</string> <string name="label_go_key" msgid="1635148082137219148">"Ugrás"</string> <string name="label_next_key" msgid="362972844525672568">"Tovább"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hangbevivel KI"</string> <string name="configure_input_method" msgid="373356270290742459">"Beviteli módok beállítása"</string> <string name="language_selection_title" msgid="1651299598555326750">"Beviteli nyelvek"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Időbélyegző naplózáskor"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Rögzített időbélyegzők"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Ne naplózza"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Napló törlése folyamatban"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Napló törölve"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Napló NINCS törölve"</string> <string name="select_language" msgid="3693815588777926848">"Beviteli nyelvek"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Érintse meg újból a mentéshez"</string> <string name="has_dictionary" msgid="6071847973466625007">"Van elérhető szótár"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index e0e92b5bd..ee4651c9e 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Keyboard Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Keyboard Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Setelan keyboard Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Pemeriksa ejaan Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Pemeriksa ejaan Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Setelan pemeriksaan ejaan"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kontak"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pemeriksa ejaan menggunakan entri dari daftar kontak Anda"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar jika tombol ditekan"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</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="enable_span_insert" msgid="7204653105667167620">"Aktifkan koreksi ulang"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setel saran untuk koreksi ulang"</string> <string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus pengaya"</string> <string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sangat agresif"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Saran kata berikutnya"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gunakan kata sebelumnya untuk meningkatkan saran"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Prediksi kata berikutnya"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan kata sebelumnya juga untuk prediksi"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Saran kata berikutnya"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan kata sebelumnya"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Telah disimpan"</string> <string name="label_go_key" msgid="1635148082137219148">"Buka"</string> <string name="label_next_key" msgid="362972844525672568">"Berikutnya"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Masukan suara dinonaktifkan"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurasikan metode masukan"</string> <string name="language_selection_title" msgid="1651299598555326750">"Bahasa masukan"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Catat cap waktu di log"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Cap waktu yang direkam"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Jangan simpan log sesi ini"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Menghapus log sesi"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Log sesi dihapus"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Log sesi BELUM dihapus"</string> <string name="select_language" msgid="3693815588777926848">"Bahasa masukan"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Sentuh lagi untuk menyimpan"</string> <string name="has_dictionary" msgid="6071847973466625007">"Kamus yang tersedia"</string> diff --git a/java/res/values-it/strings-appname.xml b/java/res/values-it/strings-appname.xml new file mode 100644 index 000000000..838daaf40 --- /dev/null +++ b/java/res/values-it/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Controllo ortografico Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Impostazioni tastiera Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Impostazioni di controllo ortografico"</string> +</resources> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index a54958c05..e52e250d2 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Tastiera Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tastiera Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Controllo ortografico Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Controllo ortografico Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Impostazioni di controllo ortografico"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca in nomi contatti"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"La funzione di controllo ortografico usa voci dell\'elenco contatti"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</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="enable_span_insert" msgid="7204653105667167620">"Attiva nuove correzioni"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Imposta suggerimenti per nuove correzioni"</string> <string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dizionari aggiuntivi"</string> <string name="main_dictionary" msgid="4798763781818361168">"Dizionario principale"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Media"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Massima"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Massima"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Suggerimenti parola successiva"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usa parola precedente per migliorare suggerimenti"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Previsione parola successiva"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usa anche la parola precedente per la previsione"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Suggerimenti parola successiva"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"In base alla parola precedente"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string> <string name="label_go_key" msgid="1635148082137219148">"Vai"</string> <string name="label_next_key" msgid="362972844525672568">"Avanti"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Comandi vocali disatt."</string> <string name="configure_input_method" msgid="373356270290742459">"Configura metodi di immissione"</string> <string name="language_selection_title" msgid="1651299598555326750">"Lingue comandi"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Indicazione temporale log"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Indicazione temporale registrata"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Non registrare la sessione"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Eliminazione log sessione"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Log di sessione eliminato"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Log sessione non eliminato"</string> <string name="select_language" msgid="3693815588777926848">"Lingue comandi"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Tocca di nuovo per salvare"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dizionario disponibile"</string> diff --git a/java/res/values-iw/strings-appname.xml b/java/res/values-iw/strings-appname.xml new file mode 100644 index 000000000..92bc240f1 --- /dev/null +++ b/java/res/values-iw/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"בודק האיות של Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"הגדרות מקלדת Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"הגדרות בדיקת איות"</string> +</resources> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index 323cac198..662f2f462 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"מקלדת Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"מקלדת Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"הגדרות מקלדת של Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"בודק האיות של Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"בודק האיות של Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"הגדרות בדיקת איות"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"חפש שמות של אנשי קשר"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"בודק האיות משתמש בערכים מרשימת אנשי הקשר שלך"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"רטט בלחיצה על מקשים"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ברירת מחדל"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"הצע שמות של אנשי קשר"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"השתמש בשמות מרשימת אנשי הקשר עבור הצעות ותיקונים"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"הפוך תיקונים חוזרים לפעילים"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"הגדר הצעות עבור תיקונים חוזרים"</string> <string name="auto_cap" msgid="1719746674854628252">"הפיכת אותיות לרישיות באופן אוטומטי"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"הוספת מילונים"</string> <string name="main_dictionary" msgid="4798763781818361168">"מילון ראשי"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"מצומצם"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"מחמיר"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"מחמיר מאוד"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"הצעות המילה הבאה"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"השתמש במילה הקודמת כדי לשפר את ההצעות"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"חיזוי המילה הבאה"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"השתמש במילה הקודמת גם עבור חיזוי"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"הצעות המילה הבאה"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"בהתבסס על המילה הקודמת"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : נשמרה"</string> <string name="label_go_key" msgid="1635148082137219148">"בצע"</string> <string name="label_next_key" msgid="362972844525672568">"הבא"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"הקלט הקולי מושבת"</string> <string name="configure_input_method" msgid="373356270290742459">"הגדרת שיטות קלט"</string> <string name="language_selection_title" msgid="1651299598555326750">"שפות קלט"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"ציין חותמת זמן ביומן"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"חותמת זמן מתועדת"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"אל תרשום הפעלה זו ביומן"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"מוחק יומן הפעלה"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"יומן הפעלה נמחק"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"יומן הפעלה לא נמחק"</string> <string name="select_language" msgid="3693815588777926848">"שפות קלט"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"גע שוב כדי לשמור"</string> <string name="has_dictionary" msgid="6071847973466625007">"מילון זמין"</string> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 3a865166f..050ca66ec 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Androidキーボード"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Androidキーボード(AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボードの設定"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidスペルチェッカー"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidスペルチェッカー(AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"スペルチェックの設定"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"連絡先名の検索"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"スペルチェッカーでは連絡先リストのエントリを使用します"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"デフォルト"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"候補の連絡先名を表示"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"連絡先の名前を使用して候補表示や自動修正を行います"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"再修正を有効にする"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"再修正の候補を挿入する"</string> <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"アドオン辞書"</string> <string name="main_dictionary" msgid="4798763781818361168">"メイン辞書"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"中"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"強"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"最も強い"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"次の入力候補"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"直前の単語から入力候補を予測します"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"次の入力候補を予測"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"前の語句も予測に使用"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"次の入力候補"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"前の語句に基づいた入力候補表示"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string> <string name="label_go_key" msgid="1635148082137219148">"実行"</string> <string name="label_next_key" msgid="362972844525672568">"次へ"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"音声入力は無効です"</string> <string name="configure_input_method" msgid="373356270290742459">"入力方法を設定"</string> <string name="language_selection_title" msgid="1651299598555326750">"入力言語"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"タイムスタンプを記録"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"タイムスタンプ記録済み"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"セッションを記録しない"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"セッションログ削除中"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"セッションログ削除済み"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"セッションログ未削除"</string> <string name="select_language" msgid="3693815588777926848">"入力言語"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"保存するにはもう一度タップ"</string> <string name="has_dictionary" msgid="6071847973466625007">"辞書を利用できます"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 536f06dbd..516c8c382 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android 키보드"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 키보드(AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android 키보드 설정"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 맞춤법 검사기"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 맞춤법 검사기(AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"맞춤법 검사 설정"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"연락처 이름 조회"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"맞춤법 검사기가 주소록의 항목을 사용합니다."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"주소록 이름 활용"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"추천 및 수정에 주소록의 이름 사용"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"재수정 가능 설정"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"재수정 추천어 사전 활성화"</string> <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"사전 추가"</string> <string name="main_dictionary" msgid="4798763781818361168">"기본 사전"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"약"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"중"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"강"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"다음 추천 검색어"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"이전 단어를 사용하여 추천 검색어 개선"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"다음 예상 검색어"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"이전 단어를 사용하여 예상 검색어 표시"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"다음 검색어 추천"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"이전 단어에 기반한 추천"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: 저장됨"</string> <string name="label_go_key" msgid="1635148082137219148">"이동"</string> <string name="label_next_key" msgid="362972844525672568">"다음"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"음성 입력이 사용 중지됨"</string> <string name="configure_input_method" msgid="373356270290742459">"입력 방법 설정"</string> <string name="language_selection_title" msgid="1651299598555326750">"입력 언어"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"로그에 타임스탬프를 기록"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"타임스탬프를 기록함"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"이 세션을 로그하지 마세요."</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"세션 로그 삭제"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"세션 로그가 삭제됨"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"세션 로그가 삭제되지 않음"</string> <string name="select_language" msgid="3693815588777926848">"입력 언어"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"저장하려면 다시 터치"</string> <string name="has_dictionary" msgid="6071847973466625007">"사전 사용 가능"</string> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index a0b8fc87d..08d66595b 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"„Android“ klaviatūra"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"„Android“ klaviatūra (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"„Android“ klaviatūros nustatymai"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"„Android“ rašybos tikrinimo programa"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"„Android“ rašybos tikrinimo programa (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Rašybos tikrinimo nustatymai"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktų vardų paieška"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rašybos tikrinimo progr. naudoja įrašus, esančius kontaktų sąraše"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibruoti, kai paspaudžiami klavišai"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</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="enable_span_insert" msgid="7204653105667167620">"Įdiegti pakartotinius pataisymus"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nustatyti pakartotinio pataisymo pasiūlymus"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildomi žodynai"</string> <string name="main_dictionary" msgid="4798763781818361168">"Pagrindinis žodynas"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Vidutinis"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Atkaklus"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Labai agresyviai"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Kito žodžio pasiūlymai"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Naudoti ankstesnį žodį pasiūlymams patobulinti"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Kito žodžio numatymas"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Numatant naudoti ir ankstesnį žodį"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Kito žodžio pasiūlymai"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Pagal ankstesnį žodį"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: išsaugota"</string> <string name="label_go_key" msgid="1635148082137219148">"Pradėti"</string> <string name="label_next_key" msgid="362972844525672568">"Kitas"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balso įv. neleidž."</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigūruoti įvesties metodus"</string> <string name="language_selection_title" msgid="1651299598555326750">"Įvesties kalbos"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Pažym. laiko žymę žurnale"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Įrašyta laiko žymė"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Neįrašyti šios sesijos"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Ištrinam. sesijos žurnal."</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Sesijos žurnalas ištrint."</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Sesij. žurnal. NEIŠTRINT."</string> <string name="select_language" msgid="3693815588777926848">"Įvesties kalbos"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Jei norite išsaugoti, palieskite dar kartą"</string> <string name="has_dictionary" msgid="6071847973466625007">"Žodynas galimas"</string> diff --git a/java/res/values-lv/strings-appname.xml b/java/res/values-lv/strings-appname.xml new file mode 100644 index 000000000..fe2f33697 --- /dev/null +++ b/java/res/values-lv/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android pareizrakstības pārbaudītājs"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Android tastatūras iestatījumi"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Pareizrakstības pārbaudes iestatījumi"</string> +</resources> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 93727a8ba..3685cccbe 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android tastatūra"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android tastatūra (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android tastatūras iestatījumi"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android pareizrakstības pārbaudītājs"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android pareizrakstības pārbaudītājs (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Pareizrakstības pārbaudes iestatījumi"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Meklēt kontaktp. vārdus"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pareizrakst. pārbaudītājs lieto ierakstus no kontaktp. saraksta."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrēt, nospiežot taustiņu"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</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="enable_span_insert" msgid="7204653105667167620">"Iespējot atk. labojumus"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Iestatīt atkārtotu labojumu ieteikumus"</string> <string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildinājumu vārdnīcas"</string> <string name="main_dictionary" msgid="4798763781818361168">"Galvenā vārdnīca"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mērena"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresīva"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Ļoti radikāla"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Nākamā vārda ieteikumi"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Ieteikumu uzlabošanai izmantot iepriekšējo vārdu"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Nākamā vārda prognozēšana"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Izmantot iepriekšējo vārdu arī prognozēm"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Nākamie vārdu ieteikumi"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Pamatojoties uz iepriekšējo vārdu"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: saglabāts"</string> <string name="label_go_key" msgid="1635148082137219148">"Sākt"</string> <string name="label_next_key" msgid="362972844525672568">"Tālāk"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balss iev. atspējota"</string> <string name="configure_input_method" msgid="373356270290742459">"Ievades metožu konfigurēšana"</string> <string name="language_selection_title" msgid="1651299598555326750">"Ievades valodas"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Atzīmēt laiksp. žurnālā"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Laikspied. ir reģistrēts."</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Nereģistrēt šo sesiju"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Not. sesijas žurn. dzēš."</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Sesijas žurnāls ir dzēsts"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Sesijas žurn. NAV dzēsts"</string> <string name="select_language" msgid="3693815588777926848">"Ievades valodas"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Pieskarieties vēlreiz, lai saglabātu."</string> <string name="has_dictionary" msgid="6071847973466625007">"Ir pieejama vārdnīca."</string> diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml index a07c44f2c..59fec8845 100644 --- a/java/res/values-ms/strings.xml +++ b/java/res/values-ms/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Papan kekunci Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Papan kekunci Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Tetapan papan kekunci Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Penyemak ejaan Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Penyemak ejaan Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Tetapan penyemakan ejaan"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kenalan"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Penyemak ejaan menggunakan entri dari senarai kenalan anda"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar pada tekanan kekunci"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string> <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="enable_span_insert" msgid="7204653105667167620">"Dayakan pembetulan semula"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Tetapkan cadangan untuk pembetulan semula"</string> <string name="auto_cap" msgid="1719746674854628252">"Huruf besar auto"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus tambahan"</string> <string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sangat agresif"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Cadangan perkataan seterusnya"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gunakan perkataan sebelumnya untuk memperbaik cadangan"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Ramalan perkataan seterusnya"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan juga perkataan sebelumnya untuk ramalan"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Cadangan perkataan seterusnya"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan perkataan sebelumnya"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Disimpan"</string> <string name="label_go_key" msgid="1635148082137219148">"Pergi"</string> <string name="label_next_key" msgid="362972844525672568">"Seterusnya"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Input suara dilmphkn"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurasikan kaedah input"</string> <string name="language_selection_title" msgid="1651299598555326750">"Bahasa input"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Tanda cap waktu dalam log"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Cap waktu direkodkan"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Jangan log sesi ini"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Memadam log sesi"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Log sesi dipadam"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Log sesi TIDAK dipadam"</string> <string name="select_language" msgid="3693815588777926848">"Bahasa input"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Sentuh lagi untuk menyimpan"</string> <string name="has_dictionary" msgid="6071847973466625007">"Kamus tersedia"</string> diff --git a/java/res/values-nb/strings-appname.xml b/java/res/values-nb/strings-appname.xml new file mode 100644 index 000000000..b5ed18abb --- /dev/null +++ b/java/res/values-nb/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-stavekontroll"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Innstillinger for Android-tastatur"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Innstillinger for stavekontroll"</string> +</resources> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 75c1295a9..2f4194ac0 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Skjermtastatur"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-tastatur (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontroll"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-stavekontroll (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Innstillinger for stavekontroll"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå opp kontaktnavn"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruker oppføringer fra kontaktlisten din"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</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="enable_span_insert" msgid="7204653105667167620">"Aktiver korrigeringer"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angi forslag for korrigeringer"</string> <string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilleggsordbøker"</string> <string name="main_dictionary" msgid="4798763781818361168">"Hovedordliste"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Omfattende"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Veldig aggressiv"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Forslag til rettelser av neste ord"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Bruk forrige ord til å forbedre forslagene"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Forslag til neste ord"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Bruk forrige ord også for forslag"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Forslag til neste ord"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Basert på det forrige ordet"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string> <string name="label_go_key" msgid="1635148082137219148">"Utfør"</string> <string name="label_next_key" msgid="362972844525672568">"Neste"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Taleinndata er deaktiv."</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inndatametoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inndataspråk"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Notér tidsstempel i logg"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Registrerte tidsstempel"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Ikke loggfør denne økten"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Sletter øktloggen"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Øktloggen ble slettet"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Øktloggen ble IKKE slettet"</string> <string name="select_language" msgid="3693815588777926848">"Inndataspråk"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Trykk på nytt for å lagre"</string> <string name="has_dictionary" msgid="6071847973466625007">"Ordbok tilgjengelig"</string> diff --git a/java/res/values-nl/strings-appname.xml b/java/res/values-nl/strings-appname.xml new file mode 100644 index 000000000..f1cb252c2 --- /dev/null +++ b/java/res/values-nl/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Spellingcontrole van Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Instellingen voor Android-toetsenbord"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Instellingen voor spellingcontrole"</string> +</resources> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index a7d499bd0..c42cd100b 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-toetsenbord (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Spellingcontrole van Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Spellingcontrole van Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Instellingen voor spellingcontrole"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Contactnamen opzoeken"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"De spellingcontrole gebruikt items uit uw contactenlijst"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij toetsaanslag"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</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="enable_span_insert" msgid="7204653105667167620">"Verbeteringen inschakelen"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Suggesties instellen voor verbeteringen"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Woordenboeken toevoegen"</string> <string name="main_dictionary" msgid="4798763781818361168">"Algemeen woordenboek"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Normaal"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressief"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Zeer agressief"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Volgende woordsuggesties"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Vorig woord gebruiken om suggesties te verbeteren"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Volgende woordvoorspelling"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Het voorgaande woord ook voor voorspelling gebruiken"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Suggesties voor volgend woord"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Op basis van het vorige woord"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: opgeslagen"</string> <string name="label_go_key" msgid="1635148082137219148">"Start"</string> <string name="label_next_key" msgid="362972844525672568">"Verder"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spraakinvoer is uit"</string> <string name="configure_input_method" msgid="373356270290742459">"Invoermethoden configureren"</string> <string name="language_selection_title" msgid="1651299598555326750">"Invoertalen"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Tijdstempel opnemen in logbestand"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Opgenomen tijdstempel"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Sessie niet registreren"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Sessielogbestand verwijderen"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Sessielogbestand verwijderd"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Sessielogbestand NIET verwijderd"</string> <string name="select_language" msgid="3693815588777926848">"Invoertalen"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Raak nogmaals aan om op te slaan"</string> <string name="has_dictionary" msgid="6071847973466625007">"Woordenboek beschikbaar"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index bf27782b6..7c86dbfdc 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Klawiatura Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klawiatura Androida (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Słownik Androida"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Sprawdzanie pisowni na Androidzie (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ustawienia sprawdzania pisowni"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Przeszukaj kontakty"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Sprawdzanie pisowni bierze pod uwagę wpisy z listy kontaktów."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</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="enable_span_insert" msgid="7204653105667167620">"Włącz poprawki"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ustaw sugestie poprawek"</string> <string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatkowe słowniki"</string> <string name="main_dictionary" msgid="4798763781818361168">"Słownik główny"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Umiarkowana"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresywna"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Bardzo agresywna"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestie kolejnych słów"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Używaj poprzedniego wyrazu, by polepszyć sugestie"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Przewidywanie następnego wyrazu"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Przewiduj również na podstawie poprzedniego słowa"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sugestie kolejnych słów"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podstawie poprzedniego słowa"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Dalej"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Wprowadzanie głosowe jest wyłączone"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfiguruj metody wprowadzania"</string> <string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Znacznik czasu uwagi w dzienniku"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Zapisano znacznik czasu"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Nie rejestruj tej sesji"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Usuwanie dziennika sesji"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Usunięto dziennik sesji"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Dziennik sesji NIEUSUNIĘTY"</string> <string name="select_language" msgid="3693815588777926848">"Języki wprowadzania"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Dotknij ponownie, aby zapisać"</string> <string name="has_dictionary" msgid="6071847973466625007">"Słownik dostępny"</string> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index ccb042e47..06d7a78d6 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Teclado do Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Definições de teclado do Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Verificador ortográfico do Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Verificador ortográfico do Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Definições da verificação ortográfica"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Procurar nomes de contac."</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico utiliza entradas da sua lista de contactos"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</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="enable_span_insert" msgid="7204653105667167620">"Ativar correções"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para correções"</string> <string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários extras"</string> <string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiva"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muito agressivo"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestões da palavra seguinte"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilizar palavra anterior para melhorar sugestões"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Previsão da palavra seguinte"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar a palavra anterior também para predição"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sugestões da palavra seguinte"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Avançar"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. voz desact."</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de introdução"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Anotar car. data no reg."</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Carimbo de data gravado"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Não registar esta sessão"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"A eliminar reg. da sessão"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Reg. de sessão eliminado"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Reg. de sessão NÃO elim."</string> <string name="select_language" msgid="3693815588777926848">"Idiomas de introdução"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Toque novamente para guardar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index 7d64759d8..5b7c60fca 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Teclado Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Corretor ortográfico do Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corretor ortográfico do Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configurações de verificação ortográfica"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nomes de contatos"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico usa entradas de sua lista de contatos"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string> <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="enable_span_insert" msgid="7204653105667167620">"Ativar recorreções"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para recorreções"</string> <string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários complementares"</string> <string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressivo"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muito agressivo"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestões p/ palavra seguinte"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar palavra anterior para melhorar as sugestões"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Previsão da palavra seguinte"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use também a palavra anterior para prever"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sugestões para a palavra seguinte"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string> <string name="label_go_key" msgid="1635148082137219148">"Ir"</string> <string name="label_next_key" msgid="362972844525672568">"Avançar"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Texto por voz desat."</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Indicar data/hora no reg."</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Data/hora registrada"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Não registrar esta sessão"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Excluindo reg. de sessão"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Registro excluído"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Registro NÃO excluído"</string> <string name="select_language" msgid="3693815588777926848">"Idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Toque novamente para salvar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string> diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index 18741f816..41be654fe 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -20,18 +20,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Tastatura Android"</string> <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) --> <skip /> - <string name="english_ime_settings" msgid="6661589557206947774">"Parameters da la tastatura Android"</string> <!-- no translation found for english_ime_input_options (3909945612939668554) --> <skip /> - <!-- no translation found for spell_checker_service_name (7338064335159755926) --> + <!-- no translation found for english_ime_research_log (8492602295696577851) --> <skip /> <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) --> <skip /> - <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> - <skip /> <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) --> <skip /> <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) --> @@ -65,10 +61,6 @@ <skip /> <!-- no translation found for use_contacts_dict_summary (6599983334507879959) --> <skip /> - <!-- no translation found for enable_span_insert (7204653105667167620) --> - <skip /> - <!-- no translation found for enable_span_insert_summary (2947317657871394467) --> - <skip /> <string name="auto_cap" msgid="1719746674854628252">"Maiusclas automaticas"</string> <!-- no translation found for configure_dictionaries_title (4238652338556902049) --> <skip /> @@ -96,13 +88,9 @@ <skip /> <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) --> <skip /> - <!-- no translation found for bigram_suggestion (8169311444438922902) --> + <!-- no translation found for bigram_prediction (5809665643352206540) --> <skip /> - <!-- no translation found for bigram_suggestion_summary (6635527607242625713) --> - <skip /> - <!-- no translation found for bigram_prediction (3216364899483135294) --> - <skip /> - <!-- no translation found for bigram_prediction_summary (1747261921174300098) --> + <!-- no translation found for bigram_prediction_summary (3253961591626441019) --> <skip /> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Memorisà"</string> <string name="label_go_key" msgid="1635148082137219148">"Dai"</string> @@ -190,6 +178,18 @@ <!-- no translation found for configure_input_method (373356270290742459) --> <skip /> <string name="language_selection_title" msgid="1651299598555326750">"Linguas da cumonds vocals"</string> + <!-- no translation found for note_timestamp_for_researchlog (1889446857977976026) --> + <skip /> + <!-- no translation found for notify_recorded_timestamp (8036429032449612051) --> + <skip /> + <!-- no translation found for do_not_log_this_session (413762473641146336) --> + <skip /> + <!-- no translation found for notify_session_log_deleting (3299507647764414623) --> + <skip /> + <!-- no translation found for notify_session_log_deleted (8687927130100934686) --> + <skip /> + <!-- no translation found for notify_session_log_not_deleted (2592908998810755970) --> + <skip /> <!-- no translation found for select_language (3693815588777926848) --> <skip /> <!-- no translation found for hint_add_to_dictionary (573678656946085380) --> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 0c4d8bcfb..338024c5a 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Tastatură Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tastatură Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Setările tastaturii Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Verificator ortografic Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Verificator ortografic Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Setări de verificare ortografică"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Verificare nume în agendă"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Verificatorul ortografic utilizează intrări din lista de contacte"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrare la apăsarea tastei"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</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="enable_span_insert" msgid="7204653105667167620">"Activaţi rectificările"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setaţi sugestii pentru rectificări"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicţionare suplimentare"</string> <string name="main_dictionary" msgid="4798763781818361168">"Dicţionar principal"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderată"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivă"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Foarte exigentă"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestii pentru cuvântul următor"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilizaţi cuvântul anterior pentru a îmbunătăţi sugestiile"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Predicţia cuvântului următor"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Se utilizează şi cuvântul precedent pentru predicţii"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sugestii pentru cuvântul următor"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Bazate pe cuvântul precedent"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: salvat"</string> <string name="label_go_key" msgid="1635148082137219148">"OK"</string> <string name="label_next_key" msgid="362972844525672568">"Înainte"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Intr. vocală dezact."</string> <string name="configure_input_method" msgid="373356270290742459">"Configuraţi metodele de intrare"</string> <string name="language_selection_title" msgid="1651299598555326750">"Selectaţi limba"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Înreg. marc. temp. jurnal"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Marcaj temporal înregis."</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Nu înregistraţi sesiunea"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Se șterge jurnal sesiune"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Jurnal de sesiune șters"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Jurnal sesiune neşters"</string> <string name="select_language" msgid="3693815588777926848">"Limbi de intrare"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Atingeţi din nou pentru a salva"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dicţionar disponibil"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index f76cc8eaf..4dd5b31a0 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавиатура Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Клавиатура Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Настройки"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Проверка правописания Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Проверка правописания Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройка проверки правописания"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Поиск контактов"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Обращаться к списку контактов при проверке правописания"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Подсказывать имена"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Подсказывать исправления на основе имен из списка контактов"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Автоисправление"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показывать варианты исправления"</string> <string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Дополнительные словари"</string> <string name="main_dictionary" msgid="4798763781818361168">"Основной словарь"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умеренное"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активное"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Очень активно"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Следующие варианты"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Использовать предыдущее слово, чтобы исправить предложенные варианты"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Следующая подсказка"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Использовать предыдущее слово для прогнозирования"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Подсказка для следующего слова"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Подсказки, основанные на предыдущих словах"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: сохранено"</string> <string name="label_go_key" msgid="1635148082137219148">"Поиск"</string> <string name="label_next_key" msgid="362972844525672568">"Далее"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голосовой ввод откл."</string> <string name="configure_input_method" msgid="373356270290742459">"Настройка способов ввода"</string> <string name="language_selection_title" msgid="1651299598555326750">"Языки ввода"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Закладка в журнале"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Закладка сохранена"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Не сохранять этот сеанс"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Удаление…"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Запись сеанса удалена"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Запись сеанса НЕ удалена"</string> <string name="select_language" msgid="3693815588777926848">"Языки ввода"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Нажмите, чтобы сохранить"</string> <string name="has_dictionary" msgid="6071847973466625007">"Доступен словарь"</string> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 44eecdc74..1dced2099 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Klávesnica Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klávesnica Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Nastavenia klávesnice Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kontrola pravopisu Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavenia kontroly pravopisu"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhľadať kontakty"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používa záznamy z vášho zoznamu kontaktov"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Pri stlačení klávesu vibrovať"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</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="enable_span_insert" msgid="7204653105667167620">"Povoliť opätovné opravy"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastaviť návrhy pre opätovné opravy"</string> <string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplnkové slovníky"</string> <string name="main_dictionary" msgid="4798763781818361168">"Hlavný slovník"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mierne"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresívne"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Veľmi agresívne"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Návrhy ďalšieho slova"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Na zlepšenie návrhov použiť predchádzajúce slovo"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Odhad ďalšieho slova"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použiť predchádzajúce slovo aj pre predpoveď"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Návrhy ďalšieho slova"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na základe predchádzajúceho slova"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Uložené"</string> <string name="label_go_key" msgid="1635148082137219148">"Hľadať"</string> <string name="label_next_key" msgid="362972844525672568">"Ďalej"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup je zakázaný"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurovať metódy vstupu"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jazyky vstupu"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Časová pečiatka denníka"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Časová pečiatka zaznamenaná"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Neukl. reláciu do denníka"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Odstraň. denníka relácie"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Denník relácie odstránený"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Denník relácie NIE JE odstr."</string> <string name="select_language" msgid="3693815588777926848">"Jazyky vstupu"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Opätovným dotykom uložíte"</string> <string name="has_dictionary" msgid="6071847973466625007">"K dispozícii je slovník"</string> diff --git a/java/res/values-sl/strings-appname.xml b/java/res/values-sl/strings-appname.xml new file mode 100644 index 000000000..aa8c8163c --- /dev/null +++ b/java/res/values-sl/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Črkovalnik za Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Nastavitve tipkovnice Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavitve preverjanja črkovanja"</string> +</resources> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index ce7dc70bb..036802d5b 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Tipkovnica Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tipkovnica Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Nastavitve tipkovnice Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Črkovalnik za Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Črkovalnik za Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavitve preverjanja črkovanja"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Iskanje imen stikov"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Črkovalnik uporablja vnose s seznama stikov"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibriranje ob pritisku tipke"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</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="enable_span_insert" msgid="7204653105667167620">"Omogoči vnovične popravke"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavitev predlogov za vnovične popravke"</string> <string name="auto_cap" msgid="1719746674854628252">"Samod. velike začetnice"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatni slovarji"</string> <string name="main_dictionary" msgid="4798763781818361168">"Glavni slovar"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Zmerno"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Strogo"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Zelo strogo"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Predlogi naslednje besede"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Predloge izboljšaj s prejšnjo besedo"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Predvidevanje naslednje besede"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Uporabi prejšnjo besedo tudi za predvidevanje"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Predlogi za naslednjo besedo"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podlagi prejšnje besede"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: shranjeno"</string> <string name="label_go_key" msgid="1635148082137219148">"Pojdi"</string> <string name="label_next_key" msgid="362972844525672568">"Naprej"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. vnos je onem."</string> <string name="configure_input_method" msgid="373356270290742459">"Nastavitev načinov vnosa"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jeziki vnosa"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"V dnev. zabeleži čas. žig"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Časovni žig zabeležen"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Brez dnevnika za to sejo"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Brisanje seje dnevnika"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Dnevnik seje izbrisan"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Dnevnik seje NI izbrisan"</string> <string name="select_language" msgid="3693815588777926848">"Jeziki vnosa"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Dotaknite se še enkrat, da shranite"</string> <string name="has_dictionary" msgid="6071847973466625007">"Slovar je na voljo"</string> diff --git a/java/res/values-sr/strings-appname.xml b/java/res/values-sr/strings-appname.xml new file mode 100644 index 000000000..01da6d5c0 --- /dev/null +++ b/java/res/values-sr/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Android провера правописа"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Подешавања Android тастатуре"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Подешавања провере правописа"</string> +</resources> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 6d2899b47..92f275f2e 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android тастатура"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android тастатура (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Подешавања Android тастатуре"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android провера правописа"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android провера правописа (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Подешавања провере правописа"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Потражи имена контаката"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Контролор правописа користи уносе са листе контаката"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вибрирај на притисак тастера"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Предложи имена контаката"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Користи имена из Контаката за предлоге и исправке"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Омогући поновне исправке"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Подешавање предлога за поновне исправке"</string> <string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Помоћни речници"</string> <string name="main_dictionary" msgid="4798763781818361168">"Главни речник"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умерено"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агресивно"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Веома агресивно"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Предлози за следећу реч"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Користи претходну реч за побољшање предлога"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Предвиђање следеће речи"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Користи претходну реч и за предвиђање"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Предлози за следећу реч"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"На основу претходне речи"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Сачувано"</string> <string name="label_go_key" msgid="1635148082137219148">"Иди"</string> <string name="label_next_key" msgid="362972844525672568">"Следеће"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Гласовни унос је онемогућен"</string> <string name="configure_input_method" msgid="373356270290742459">"Конфигурисање метода уноса"</string> <string name="language_selection_title" msgid="1651299598555326750">"Језици за унос"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Наведи временску ознаку у евиденцији"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Снимљена временска ознака"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Не евидентирај ову сесију"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Брисање евиденције сесије"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Евиденција сесије је обрисана"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Евиденција сесије НИЈЕ избрисана"</string> <string name="select_language" msgid="3693815588777926848">"Језици уноса"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Поново додирните да бисте сачували"</string> <string name="has_dictionary" msgid="6071847973466625007">"Речник је доступан"</string> diff --git a/java/res/values-sv/strings-appname.xml b/java/res/values-sv/strings-appname.xml new file mode 100644 index 000000000..edb2af2ad --- /dev/null +++ b/java/res/values-sv/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Stavningskontroll i Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Inställningar för Androids tangentbord"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Inställningar för stavningskontroll"</string> +</resources> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index 75e80d42d..d460b5ba1 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Androids tangentbord"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Androids tangentbord (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Stavningskontroll i Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Stavningskontroll i Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Inställningar för stavningskontroll"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Sök namn på kontakter"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"I stavningskontrollen används poster från kontaktlistan"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</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="enable_span_insert" msgid="7204653105667167620">"Aktivera omkorrigeringar"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ställ in förslag för omkorrigeringar"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilläggsordlistor"</string> <string name="main_dictionary" msgid="4798763781818361168">"Huvudordlistan"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Måttlig"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Mycket aggressivt"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Föreslå nästa ord"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Förbättra förslagen med föregående ord"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Förutspå nästa ord"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Använd även föregående ord för att ge förslag"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Föreslå nästa ord"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Baserat på föregående ord"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: sparat"</string> <string name="label_go_key" msgid="1635148082137219148">"Kör"</string> <string name="label_next_key" msgid="362972844525672568">"Nästa"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Röstinmatning inaktiv"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurera inmatningsmetoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inmatningsspråk"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Markera tidpunkt i loggen"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Tidpunkten har sparats"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Logga inte detta besök"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Besöksloggen tas bort"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Besöksloggen togs bort"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Besöksloggen togs EJ bort"</string> <string name="select_language" msgid="3693815588777926848">"Inmatningsspråk"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Spara genom att trycka igen"</string> <string name="has_dictionary" msgid="6071847973466625007">"En ordlista är tillgänglig"</string> diff --git a/java/res/values-sw/strings-appname.xml b/java/res/values-sw/strings-appname.xml new file mode 100644 index 000000000..4a5b2a683 --- /dev/null +++ b/java/res/values-sw/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Kikagua tahajia cha Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Mipangilio ya kibodi ya Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Mipangilio ya kukagua tahajia"</string> +</resources> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index da51cb3b9..9727fda8b 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Kibodi ya Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Kicharazio cha Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Mipangilio ya kibodi ya Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Kikagua tahajia cha Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kikagua tahajia cha Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Mipangilio ya kukagua sarufi"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya wasiliani"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kikagua tahajia hutumia ingizo kutoka kwa orodha yako ya anwani"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tetema unabofya kitufe"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Chaguo-msingi"</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="enable_span_insert" msgid="7204653105667167620">"Wezesha masahihisho mapya"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Weka mapendekezo kwa ajili ya kusahihisha upya"</string> <string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Nyongeza za kamusi"</string> <string name="main_dictionary" msgid="4798763781818361168">"Kamusi kuu"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ya wastani"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Ya hima"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Changamfu zaidi"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Mapendekezo ya neno lifuatalo"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Tumia neno la awali ili kuboresha mapendekezo"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Utabiri wa neno lifuatalo"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Tumia neno la awali pia kwa udadisi"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Mapendekezo ya neno lifuatalo"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Kulingana na neno la awali"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Imehifadhiwa"</string> <string name="label_go_key" msgid="1635148082137219148">"Nenda"</string> <string name="label_next_key" msgid="362972844525672568">"Ifuatayo"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Uingizaji sauti umelemazwa"</string> <string name="configure_input_method" msgid="373356270290742459">"Sanidi mbinu za uingizaji"</string> <string name="language_selection_title" msgid="1651299598555326750">"Lugha za uingizaji"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Dokeza mhuri wa muda kwenye kumbukumbu"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Mhuri wa muda uliorekodiwa"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Usihifadhi kumbukumbu za kipindi hiki"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Inafuta kumbukumbu za kipindi"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Kumbukumbu za kipindi zimefutwa"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Kumbukumbu za kipindi HAZIJAFUTWA"</string> <string name="select_language" msgid="3693815588777926848">"Lugha zinazoruhusiwa"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Gusa tena ili kuhifadhi"</string> <string name="has_dictionary" msgid="6071847973466625007">"Kamusi inapatikana"</string> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index da20d6607..8c365fe95 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"แป้นพิมพ์ Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"การตั้งค่าแป้นพิมพ์ Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"แอนดรอยด์ตรวจสอบการสะกด"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"แอนดรอยด์ตรวจสอบการสะกด (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"การตั้งค่าการตรวจสอบการสะกด"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ค้นหารายชื่อติดต่อ"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"เครื่องมือตรวจการสะกดใช้รายการจากรายชื่อติดต่อของคุณ"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"สั่นเมื่อกดปุ่ม"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"แนะนำชื่อผู้ติดต่อ"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ใช้ชื่อจากรายชื่อติดต่อสำหรับคำแนะนำและการแก้ไข"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"เปิดใช้งานการแก้ไขซ้ำ"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"ตั้งค่าคำแนะนำสำหรับการแก้ไขซ้ำ"</string> <string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"พจนานุกรม Add-On"</string> <string name="main_dictionary" msgid="4798763781818361168">"พจนานุกรมหลัก"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"ปานกลาง"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"เข้มงวด"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"เข้มงวดมาก"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"คำแนะนำสำหรับคำถัดไป"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"ใช้คำก่อนหน้านี้เพื่อปรับปรุงคำแนะนำ"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"การคาดคะเนคำถัดไป"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"ใช้คำก่อนหน้านี้สำหรับการคาดคะเน"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"คำแนะนำสำหรับคำถัดไป"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"ตามคำก่อนหน้า"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : บันทึกแล้ว"</string> <string name="label_go_key" msgid="1635148082137219148">"ไป"</string> <string name="label_next_key" msgid="362972844525672568">"ถัดไป"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ปิดใช้งานป้อนข้อมูลด้วยเสียง"</string> <string name="configure_input_method" msgid="373356270290742459">"กำหนดค่าวิธีการป้อนข้อมูล"</string> <string name="language_selection_title" msgid="1651299598555326750">"ภาษาในการป้อนข้อมูล"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"จดเวลาบันทึกไว้ในบันทึก"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"บันทึกเวลาบันทึกแล้ว"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"อย่าบันทึกเซสชันนี้"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"กำลังลบบันทึกเซสชัน"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"ลบบันทึกเซสชันแล้ว"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"บันทึกเซสชันไม่ถูกลบ"</string> <string name="select_language" msgid="3693815588777926848">"ภาษาสำหรับการป้อนข้อมูล"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"แตะอีกครั้งเพื่อบันทึก"</string> <string name="has_dictionary" msgid="6071847973466625007">"มีพจนานุกรมให้ใช้งาน"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index 7d365b2dc..832dc2bb7 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Mga setting ng Android keyboard"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Pang-check ng pagbabaybay ng Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Pang-check ng pagbabaybay ng Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Mga setting ng pang-check ng pagbabaybay"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Maghanap pangalan contact"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Gumagamit pang-check pagbabaybay entry sa iyong listahan contact"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Mag-vibrate sa keypress"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</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="enable_span_insert" msgid="7204653105667167620">"Paganahin ang mga muling pagtatama"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Magtakda ng mga suhestyon para sa mga muling pagtatama"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Mga diksyunaryo na add-on"</string> <string name="main_dictionary" msgid="4798763781818361168">"Pangunahing diksyunaryo"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresibo"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Napaka-agresibo"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Mga paghuhula sa susunod na salita"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gamitin ang naunang salita para mapahusay ang mga suhestiyon"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Paghuhula sa susunod na salita"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gamitin ang nakaraang salita para din sa hula"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Mga suhestiyon sa susunod na salita"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Batay sa nakaraang salita"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Na-save"</string> <string name="label_go_key" msgid="1635148082137219148">"Punta"</string> <string name="label_next_key" msgid="362972844525672568">"Susunod"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hindi pinagana ang voice input"</string> <string name="configure_input_method" msgid="373356270290742459">"I-configure ang mga pamamaraan ng pag-input"</string> <string name="language_selection_title" msgid="1651299598555326750">"Mag-input ng mga wika"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Tandaan timestamp sa log"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Na-record na timestamp"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Huwag i-log ang session"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Tinatanggl log ng session"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Tinanggal log ng session"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"HND ntnggl log ng session"</string> <string name="select_language" msgid="3693815588777926848">"Mga wika ng input"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Pinduting muli upang i-save"</string> <string name="has_dictionary" msgid="6071847973466625007">"Available ang diksyunaryo"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index b2f73912e..abfa73656 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android klavyesi"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android klavye (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye ayarları"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android yazım denetleyici"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android yazım denetleyici (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Yazım denetimi ayarları"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kişi adlarını denetle"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Yazım denetleyici, kişi listenizdeki girişleri kullanır"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</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="enable_span_insert" msgid="7204653105667167620">"Düzeltmeleri etkinleştir"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Yeniden düzeltmeler için önerileri ayarla"</string> <string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük fark yap"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Ek sözlükler"</string> <string name="main_dictionary" msgid="4798763781818361168">"Ana sözlük"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ölçülü"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Çok geniş ölçekte"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Sonraki kelime önerileri"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Önerileri geliştirmek için önceki kelimeyi kullan"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Sonraki kelime tahmini"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Önceki kelimeyi de tahmin için kullan"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Sonraki kelime önerileri"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Önceki kelimeye dayanarak"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kaydedildi"</string> <string name="label_go_key" msgid="1635148082137219148">"Git"</string> <string name="label_next_key" msgid="362972844525672568">"İleri"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Sesle grş devre dışı"</string> <string name="configure_input_method" msgid="373356270290742459">"Giriş yöntemlerini yapılandır"</string> <string name="language_selection_title" msgid="1651299598555326750">"Giriş dilleri"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Günlüğe zaman damgası koy"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Zaman damgası kaydedildi"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Bu oturumu günlüğe kaydetme"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Oturum günlüğü siliniyor"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Oturum günlüğü silindi"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Oturum günlüğü SİLİNMEDİ"</string> <string name="select_language" msgid="3693815588777926848">"Giriş dilleri"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Kaydetmek için tekrar dokunun"</string> <string name="has_dictionary" msgid="6071847973466625007">"Sözlük kullanılabilir"</string> diff --git a/java/res/values-uk/strings-appname.xml b/java/res/values-uk/strings-appname.xml new file mode 100644 index 000000000..d77b617fa --- /dev/null +++ b/java/res/values-uk/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Засіб перевірки орфографії Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Налаштування клавіатури Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Налаштування перевірки орфографії"</string> +</resources> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index e994e1a15..9e4da5b47 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Клавіатура Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавіатура Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Налашт-ня клавіат. Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Засіб перевірки орфографії Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Засіб перевірки орфографії Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Налаштування перевірки орфографії"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукати імена контактів"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Програма перевірки правопису використ. записи зі списку контактів"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібр. при натисканні клавіш"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"Пропон. імена контактів"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Використ. імена зі списку контактів для пропозицій і виправлень"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"Увімкнути виправлення"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показувати варіанти автовиправлень"</string> <string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Додані словники"</string> <string name="main_dictionary" msgid="4798763781818361168">"Основний словник"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Помірне"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активне"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Дуже активне"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Пропозиції наступного слова"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Використати попереднє слово для покращення пропозицій"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Передбачення наступного слова"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Використовувати попереднє слово також як передбачений запит"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Пропозиції наступного слова"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"На основі попереднього слова"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : збережено"</string> <string name="label_go_key" msgid="1635148082137219148">"Іти"</string> <string name="label_next_key" msgid="362972844525672568">"Далі"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голос. ввід вимкнено"</string> <string name="configure_input_method" msgid="373356270290742459">"Налаштування методів введення"</string> <string name="language_selection_title" msgid="1651299598555326750">"Мови вводу"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Мітка часу в журналі"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Записана мітка часу"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Не реєструвати цю сесію"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Видалення журналу сесії"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Журнал сесії видалено"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Журнал сесії НЕ видалено"</string> <string name="select_language" msgid="3693815588777926848">"Мови введення"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Торкніться знову, щоб зберегти"</string> <string name="has_dictionary" msgid="6071847973466625007">"Словник доступний"</string> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 753af1840..bf096c51f 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Bàn phím Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Bàn phím Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Cài đặt bàn phím Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Trình kiểm tra chính tả Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Trình kiểm tra chính tả Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Cài đặt kiểm tra chính tả"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Tra cứu tên liên hệ"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Trình kiểm tra chính tả sử dụng các mục nhập từ danh sách liên hệ của bạn"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</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="enable_span_insert" msgid="7204653105667167620">"Bật sửa đổi lại"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Đặt đề xuất cho các sửa đổi lại"</string> <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Thêm từ điển"</string> <string name="main_dictionary" msgid="4798763781818361168">"Từ điển chính"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Đơn giản"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Linh hoạt"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Rất linh hoạt"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Đề xuất từ tiếp theo"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Sử dụng từ trước đó để cải tiến đề xuất"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Dự đoán từ tiếp theo"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Cũng sử dụng từ trước đó để dự đoán"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Đề xuất từ tiếp theo"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Dựa trên từ trước đó"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Đã lưu"</string> <string name="label_go_key" msgid="1635148082137219148">"Tìm"</string> <string name="label_next_key" msgid="362972844525672568">"Tiếp theo"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Nhập liệu bằng giọng nói đã bị tắt"</string> <string name="configure_input_method" msgid="373356270290742459">"Định cấu hình phương thức nhập"</string> <string name="language_selection_title" msgid="1651299598555326750">"Ngôn ngữ nhập"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Dấu thời gian ghi chú trong nhật ký"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Dấu thời gian đã ghi"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Không ghi nhật ký phiên này"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Đang xóa nhật ký phiên"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Đã xóa nhật ký phiên"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Nhật ký phiên KHÔNG bị xóa"</string> <string name="select_language" msgid="3693815588777926848">"Ngôn ngữ nhập"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Chạm lại để lưu"</string> <string name="has_dictionary" msgid="6071847973466625007">"Có sẵn từ điển"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index efde54103..a95d32ac0 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android 键盘"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 键盘 (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android 键盘设置"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼写检查工具"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"研究记录命令"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼写检查工具 (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"拼写检查设置"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找联系人姓名"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼写检查工具会使用您的联系人列表中的条目"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按键振动"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"联系人姓名建议"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用联系人中的姓名提供建议和更正"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"允许再次更正"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"设置建议以用于再次更正"</string> <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"附加词典"</string> <string name="main_dictionary" msgid="4798763781818361168">"主词典"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"小改"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"大改"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"改动极大"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"下一字词建议"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"使用上一字词改进建议"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"下一字词预测"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"结合前一个字词进行预测"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"下一字词建议"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"根据上一个字词提供建议"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string> <string name="label_go_key" msgid="1635148082137219148">"开始"</string> <string name="label_next_key" msgid="362972844525672568">"下一步"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"语音输入功能已停用"</string> <string name="configure_input_method" msgid="373356270290742459">"配置输入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"输入语言"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"标记记录中的时间"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"已标记时间"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"不记录本次会话"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"正在删除会话记录"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"会话记录已删除"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"未能删除会话记录"</string> <string name="select_language" msgid="3693815588777926848">"输入语言"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次触摸即可保存"</string> <string name="has_dictionary" msgid="6071847973466625007">"有可用词典"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 51df022aa..b8bb3e5d7 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Android 鍵盤"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 鍵盤 (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Android 鍵盤設定"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼字檢查"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼字檢查 (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"拼字檢查設定"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查詢聯絡人姓名"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼字檢查程式使用您的聯絡人清單項目"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string> <string name="use_contacts_dict" msgid="4435317977804180815">"建議聯絡人名稱"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"根據「聯絡人」名稱提供建議與修正"</string> - <string name="enable_span_insert" msgid="7204653105667167620">"啟用重新更正"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"設定建議供重新更正"</string> <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"外掛字典"</string> <string name="main_dictionary" msgid="4798763781818361168">"主要字典"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"更正範圍小"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"更正範圍大"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"更正範圍極大"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"下一個字詞建議"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"根據前一個字詞找出更適合的建議"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"下一個字詞預測"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"同樣使用前一個字詞進行預測"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"下一個字詞建議"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"根據上一個字詞產生"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string> <string name="label_go_key" msgid="1635148082137219148">"開始"</string> <string name="label_next_key" msgid="362972844525672568">"繼續"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"語音輸入已停用"</string> <string name="configure_input_method" msgid="373356270290742459">"設定輸入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"在紀錄中加註時間戳記"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"已記錄時間戳記"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"不要記錄這個工作階段"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"正在刪除工作階段紀錄"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"已刪除工作階段紀錄"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"「未」刪除工作階段紀錄"</string> <string name="select_language" msgid="3693815588777926848">"輸入語言"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次輕觸即可儲存"</string> <string name="has_dictionary" msgid="6071847973466625007">"可使用字典"</string> diff --git a/java/res/values-zu/strings-appname.xml b/java/res/values-zu/strings-appname.xml new file mode 100644 index 000000000..b5212a5b6 --- /dev/null +++ b/java/res/values-zu/strings-appname.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for english_ime_name (178705338187710493) --> + <skip /> + <string name="spell_checker_service_name" msgid="6268342166872202903">"Isihloli sokupela se-Android"</string> + <string name="english_ime_settings" msgid="7470027018752707691">"Izilungiselelo zekhibhodi ye-Android"</string> + <string name="android_spell_checker_settings" msgid="8397842018475560441">"Izilungiselelo zokuhlola ukupela"</string> +</resources> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index d3f80e42a..6984631d3 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -20,13 +20,10 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="7252517407088836577">"Ikhibhodi ye-Android"</string> <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Ikhibhodi ye-Android (AOSP)"</string> - <string name="english_ime_settings" msgid="6661589557206947774">"Izilungiselelo zekhibhodi ye-Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string> - <string name="spell_checker_service_name" msgid="7338064335159755926">"Isihloli sokupela se-Android"</string> + <string name="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string> <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Isihloli sokupela se-Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="5822324635435443689">"Izilungiselelo zokuhlola ukupela"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Bheka amagama woxhumana nabo"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Isihloli sokupela sisebenzisa okungenayo kusuka kuhlu lalabo oxhumana nabo"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Dlidlizelisa ngokucindezela inkinobho"</string> @@ -45,8 +42,6 @@ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Okuzenzakalelayo"</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="enable_span_insert" msgid="7204653105667167620">"Vumela ukulungiswa kabusha"</string> - <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setha iziphakamiso zokulungisa kabusha"</string> <string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"Faka izichazamazwi"</string> <string name="main_dictionary" msgid="4798763781818361168">"Isichazamazwi sakho ngqangi"</string> @@ -61,10 +56,8 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Thobekile"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Bukhali"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Nobudlova kakhulu"</string> - <string name="bigram_suggestion" msgid="8169311444438922902">"Iziphakamiso zegama elilandelayo"</string> - <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Sebenzisa igama elandulele ukuthuthukisa iziphakamiso"</string> - <string name="bigram_prediction" msgid="3216364899483135294">"Ukuqagela kwegama elilandelayo"</string> - <string name="bigram_prediction_summary" msgid="1747261921174300098">"Sebenzisa igama langaphambilini ukuze uqagele"</string> + <string name="bigram_prediction" msgid="5809665643352206540">"Iziphakamiso zegama elilandelayo"</string> + <string name="bigram_prediction_summary" msgid="3253961591626441019">"Ngokususela egameni langaphambilini"</string> <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kulondoloziwe"</string> <string name="label_go_key" msgid="1635148082137219148">"Iya"</string> <string name="label_next_key" msgid="362972844525672568">"Okulandelayo"</string> @@ -111,6 +104,12 @@ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Okufakwayo ngezwi kuvinjelwe"</string> <string name="configure_input_method" msgid="373356270290742459">"Misa izindlela zokufakwayo"</string> <string name="language_selection_title" msgid="1651299598555326750">"Izilimi zokufakwayo"</string> + <string name="note_timestamp_for_researchlog" msgid="1889446857977976026">"Qaphela isitembu sesikhathi efayeleni lokungena"</string> + <string name="notify_recorded_timestamp" msgid="8036429032449612051">"Isitembu sesikhathi esirekhodiwe"</string> + <string name="do_not_log_this_session" msgid="413762473641146336">"Ungenzi ifayela lokungena lalesi sikhathi"</string> + <string name="notify_session_log_deleting" msgid="3299507647764414623">"Isusa ifayela lokungena lesikhathi"</string> + <string name="notify_session_log_deleted" msgid="8687927130100934686">"Ifayela lokungena lesikhathi lisusiwe"</string> + <string name="notify_session_log_not_deleted" msgid="2592908998810755970">"Ifayela lokungena lesikhathi alisusiwe"</string> <string name="select_language" msgid="3693815588777926848">"Izilimi zokufakwayo"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Thinta futhi ukuze ulondoloze"</string> <string name="has_dictionary" msgid="6071847973466625007">"Isichazamazwi siyatholakala"</string> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 8d3319dae..589830d8e 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -22,13 +22,10 @@ <bool name="config_use_fullscreen_mode">false</bool> <bool name="config_enable_show_voice_key_option">true</bool> <bool name="config_enable_show_popup_on_keypress_option">true</bool> - <bool name="config_enable_next_word_suggestions_option">true</bool> - <bool name="config_enable_usability_study_mode_option">false</bool> + <!-- TODO: Disable the following configuration for production. --> + <bool name="config_enable_usability_study_mode_option">true</bool> <!-- Whether or not Popup on key press is enabled by default --> <bool name="config_default_popup_preview">true</bool> - <!-- Default value for next word suggestion: while showing suggestions for a word should we weigh - in the previous word? --> - <bool name="config_default_next_word_suggestions">true</bool> <!-- Default value for next word prediction: after entering a word and a space only, should we look at input history to suggest a hopefully helpful suggestions for the next word? --> <bool name="config_default_next_word_prediction">true</bool> diff --git a/java/res/values/strings-appname.xml b/java/res/values/strings-appname.xml new file mode 100644 index 000000000..19aaa2513 --- /dev/null +++ b/java/res/values/strings-appname.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources> + <!-- Title for Latin Keyboard --> + <string name="english_ime_name">Android keyboard</string> + + <!-- Name of Android spell checker service --> + <string name="spell_checker_service_name">Android spell checker</string> + + <!-- Title for Latin keyboard settings activity / dialog --> + <string name="english_ime_settings">Android keyboard settings</string> + + <!-- Title for the spell checking service settings screen --> + <string name="android_spell_checker_settings">Spell checking settings</string> +</resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index d51d3789a..5fd4af4f4 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -18,23 +18,16 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Title for Latin keyboard --> - <string name="english_ime_name">Android keyboard</string> <!-- Application name for opensource Android keyboard. AOSP(Android Open Source Project) should not be translated. --> <string name="aosp_android_keyboard_ime_name">Android keyboard (AOSP)</string> - <!-- Title for Latin keyboard settings activity / dialog --> - <string name="english_ime_settings">Android keyboard settings</string> <!-- Title for Latin keyboard input options dialog [CHAR LIMIT=25] --> <string name="english_ime_input_options">Input options</string> + <!-- Title for Latin keyboard research log dialog, which contains special commands for users that contribute data for research. [CHAR LIMIT=33] --> + <string name="english_ime_research_log">Research Log Commands</string> - <!-- Name of Android spell checker service --> - <string name="spell_checker_service_name">Android spell checker</string> <!-- Name of Android spell checker service. AOSP(Android Open Source Project) should not be translated. --> <string name="aosp_spell_checker_service_name">Android spell checker (AOSP)</string> - <!-- Title for the spell checking service settings screen --> - <string name="android_spell_checker_settings">Spell checking settings</string> - <!-- Title for the spell checker option to turn on/off contact names lookup [CHAR LIMIT=25] --> <string name="use_contacts_for_spellchecking_option_title">Look up contact names</string> @@ -83,11 +76,6 @@ <!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=65] --> <string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string> - <!-- Option name for enabling insertion of suggestion spans (advanced option) [CHAR LIMIT=25] --> - <string name="enable_span_insert">Enable recorrections</string> - <!-- Option summary for enabling insertion of suggestion spans (advanced option) [CHAR LIMIT=65] --> - <string name="enable_span_insert_summary">Set suggestions for recorrections</string> - <!-- Option to enable auto capitalization of sentences --> <string name="auto_cap">Auto-capitalization</string> @@ -118,14 +106,10 @@ <!-- Option to suggest auto correction suggestions very aggressively. Auto-corrects to a word which has even large edit distance from typed word. [CHAR LIMIT=20] --> <string name="auto_correction_threshold_mode_very_aggeressive">Very aggressive</string> - <!-- Option to enable next word correction --> - <string name="bigram_suggestion">Next word suggestions</string> - <!-- Option to enable next word suggestion. This uses the previous word in an attempt to improve the suggestions quality --> - <string name="bigram_suggestion_summary">Use previous word to improve suggestions</string> - <!-- Option to enable using next word prediction --> - <string name="bigram_prediction">Next word prediction</string> - <!-- Description for "next word prediction" option. This displays suggestions even when there is no input, based on the previous word. --> - <string name="bigram_prediction_summary">Use previous word also for prediction</string> + <!-- Option to enable using next word suggestions. After the user types a space, with this option on, the keyboard will try to predict the next word. --> + <string name="bigram_prediction">Next word suggestions</string> + <!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. --> + <string name="bigram_prediction_summary">Based on previous word</string> <!-- Indicates that a word has been added to the dictionary --> <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string> @@ -233,6 +217,20 @@ <!-- Title for input language selection screen --> <string name="language_selection_title">Input languages</string> + <!-- Title for dialog option that lets user mark a particular time in the log for later review by experts [CHAR LIMIT=38] --> + <string name="note_timestamp_for_researchlog">Note timestamp in log</string> + <!-- Toast notification message that the time has been marked for later review. [CHAR LIMIT=25] --> + <string name="notify_recorded_timestamp">Recorded timestamp</string> + + <!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] --> + <string name="do_not_log_this_session">Do not log this session</string> + <!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] --> + <string name="notify_session_log_deleting">Deleting session log</string> + <!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] --> + <string name="notify_session_log_deleted">Session log deleted</string> + <!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] --> + <string name="notify_session_log_not_deleted">Session log NOT deleted</string> + <!-- Preference for input language selection --> <string name="select_language">Input languages</string> @@ -289,7 +287,7 @@ <string name="subtype_locale">Language</string> <!-- Title of the spinner for choosing a keyboard layout of custom style in the settings dialog [CHAR LIMIT=15] --> <string name="keyboard_layout_set">Layout</string> - <!-- The message of the dialog to note that a custom input style needs to be enabled. [CHAR LIMIT=64] --> + <!-- The message of the dialog to note that a custom input style needs to be enabled. [CHAR LIMIT=130] --> <string name="custom_input_style_note_message">"Your custom input style needs to be enabled before you start using it. Do you want to enable it now?"</string> <!-- Title of the button to enable a custom input style entry in the settings dialog [CHAR LIMIT=15] --> <string name="enable">Enable</string> diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index a1b2eb475..bf2e76a6b 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -133,6 +133,17 @@ latin:keyIconPreview="!icon/tab_key_preview" latin:backgroundType="functional" /> </case> + <case + latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked" + latin:navigateNext="true" + > + <key-style + latin:styleName="tabKeyStyle" + latin:code="!code/key_action_next" + latin:keyIcon="!icon/tab_key" + latin:keyIconPreview="!icon/tab_key_preview" + latin:backgroundType="functional" /> + </case> <default> <key-style latin:styleName="tabKeyStyle" diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml index 40082ac35..7afe5848a 100644 --- a/java/res/xml-sw768dp/key_styles_common.xml +++ b/java/res/xml-sw768dp/key_styles_common.xml @@ -117,6 +117,17 @@ latin:keyLabelFlags="fontNormal|preserveCase" latin:backgroundType="functional" /> </case> + <case + latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked" + latin:navigateNext="true" + > + <key-style + latin:styleName="tabKeyStyle" + latin:code="!code/key_action_next" + latin:keyLabel="!text/label_tab_key" + latin:keyLabelFlags="fontNormal|preserveCase" + latin:backgroundType="functional" /> + </case> <default> <key-style latin:styleName="tabKeyStyle" diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index 622da2120..162119dab 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -22,23 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <!-- Base key style for the key which may have settings or tab key as popup key. --> - <switch> - <case - latin:clobberSettingsKey="true" - > - <key-style - latin:styleName="f1MoreKeysStyle" - latin:backgroundType="functional" /> - </case> - <!-- clobberSettingsKey="false" --> - <default> - <key-style - latin:styleName="f1MoreKeysStyle" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/settings_as_more_key" - latin:backgroundType="functional" /> - </default> - </switch> + <include + latin:keyboardLayout="@xml/key_styles_f1" /> <!-- Functional key styles --> <switch> <case diff --git a/java/res/xml/key_styles_f1.xml b/java/res/xml/key_styles_f1.xml new file mode 100644 index 000000000..8dfc3cb84 --- /dev/null +++ b/java/res/xml/key_styles_f1.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- Base key style for the key which may have settings or tab key as popup key. --> + <!-- Kept as a separate file for cleaner overriding by an overlay. --> + <switch> + <case + latin:clobberSettingsKey="true" + > + <key-style + latin:styleName="f1MoreKeysStyle" + latin:backgroundType="functional" /> + </case> + <!-- clobberSettingsKey="false" --> + <default> + <key-style + latin:styleName="f1MoreKeysStyle" + latin:keyLabelFlags="hasPopupHint" + latin:moreKeys="!text/settings_as_more_key" + latin:backgroundType="functional" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 137981949..bf8805875 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -81,6 +81,12 @@ android:title="@string/misc_category" android:key="misc_settings"> <CheckBoxPreference + android:key="next_word_prediction" + android:title="@string/bigram_prediction" + android:summary="@string/bigram_prediction_summary" + android:persistent="true" + android:defaultValue="true" /> + <CheckBoxPreference android:key="usability_study_mode" android:title="@string/prefs_usability_study_mode" android:persistent="true" @@ -114,24 +120,6 @@ android:summary="@string/use_contacts_dict_summary" android:persistent="true" android:defaultValue="true" /> - <CheckBoxPreference - android:key="next_word_suggestion" - android:title="@string/bigram_suggestion" - android:summary="@string/bigram_suggestion_summary" - android:persistent="true" - android:defaultValue="true" /> - <CheckBoxPreference - android:key="next_word_prediction" - android:title="@string/bigram_prediction" - android:summary="@string/bigram_prediction_summary" - android:persistent="true" - android:defaultValue="true" /> - <CheckBoxPreference - android:key="enable_span_insert" - android:title="@string/enable_span_insert" - android:summary="@string/enable_span_insert_summary" - android:persistent="true" - android:defaultValue="true" /> <PreferenceScreen android:key="pref_vibration_duration_settings" android:title="@string/prefs_keypress_vibration_duration_settings"/> diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 23acb8b74..5ffd94a43 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -19,6 +19,7 @@ package com.android.inputmethod.accessibility; import android.content.Context; import android.text.TextUtils; import android.util.Log; +import android.util.SparseIntArray; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; @@ -39,8 +40,8 @@ public class KeyCodeDescriptionMapper { // Map of key labels to spoken description resource IDs private final HashMap<CharSequence, Integer> mKeyLabelMap; - // Map of key codes to spoken description resource IDs - private final HashMap<Integer, Integer> mKeyCodeMap; + // Sparse array of spoken description resource IDs indexed by key codes + private final SparseIntArray mKeyCodeMap; public static void init() { sInstance.initInternal(); @@ -52,7 +53,7 @@ public class KeyCodeDescriptionMapper { private KeyCodeDescriptionMapper() { mKeyLabelMap = new HashMap<CharSequence, Integer>(); - mKeyCodeMap = new HashMap<Integer, Integer>(); + mKeyCodeMap = new SparseIntArray(); } private void initInternal() { @@ -60,7 +61,7 @@ public class KeyCodeDescriptionMapper { mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); // Symbols that most TTS engines can't speak - mKeyCodeMap.put((int) ' ', R.string.spoken_description_space); + mKeyCodeMap.put(' ', R.string.spoken_description_space); // Special non-character codes defined in Keyboard mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete); @@ -273,7 +274,8 @@ public class KeyCodeDescriptionMapper { return context.getString(OBSCURED_KEY_RES_ID); } - if (mKeyCodeMap.containsKey(code)) { + final int resId = mKeyCodeMap.get(code); + if (resId != 0) { return context.getString(mKeyCodeMap.get(code)); } else if (isDefinedNonCtrl) { return Character.toString((char) code); diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 13e909c7e..9abc79d3c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -53,6 +53,7 @@ public class KeyDetector { return x + mCorrectionX; } + // TODO: Remove vertical correction. public int getTouchY(int y) { return y + mCorrectionY; } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 21f175d7d..1aec00129 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -23,6 +23,8 @@ import android.content.res.XmlResourceParser; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TypedValue; import android.util.Xml; import android.view.InflateException; @@ -44,7 +46,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.Locale; @@ -89,7 +90,8 @@ public class Keyboard { private static final int MINIMUM_LETTER_CODE = CODE_TAB; /** Special keys code. Must be negative. - * These should be aligned with values/keycodes.xml + * These should be aligned with KeyboardCodesSet.ID_TO_NAME[], + * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[] */ public static final int CODE_SHIFT = -1; public static final int CODE_SWITCH_ALPHA_SYMBOL = -2; @@ -101,8 +103,9 @@ public class Keyboard { public static final int CODE_ACTION_NEXT = -8; public static final int CODE_ACTION_PREVIOUS = -9; public static final int CODE_LANGUAGE_SWITCH = -10; + public static final int CODE_RESEARCH = -11; // Code value representing the code is not specified. - public static final int CODE_UNSPECIFIED = -11; + public static final int CODE_UNSPECIFIED = -12; public final KeyboardId mId; public final int mThemeId; @@ -132,7 +135,7 @@ public class Keyboard { public final Key[] mAltCodeKeysWhileTyping; public final KeyboardIconsSet mIconsSet; - private final HashMap<Integer, Key> mKeyCache = new HashMap<Integer, Key>(); + private final SparseArray<Key> mKeyCache = new SparseArray<Key>(); private final ProximityInfo mProximityInfo; private final boolean mProximityCharsCorrectionEnabled; @@ -182,23 +185,25 @@ public class Keyboard { if (code == CODE_UNSPECIFIED) { return null; } - final Integer keyCode = code; - if (mKeyCache.containsKey(keyCode)) { - return mKeyCache.get(keyCode); - } + synchronized (mKeyCache) { + final int index = mKeyCache.indexOfKey(code); + if (index >= 0) { + return mKeyCache.valueAt(index); + } - for (final Key key : mKeys) { - if (key.mCode == code) { - mKeyCache.put(keyCode, key); - return key; + for (final Key key : mKeys) { + if (key.mCode == code) { + mKeyCache.put(code, key); + return key; + } } + mKeyCache.put(code, null); + return null; } - mKeyCache.put(keyCode, null); - return null; } public boolean hasKey(Key aKey) { - if (mKeyCache.containsKey(aKey)) { + if (mKeyCache.indexOfValue(aKey) >= 0) { return true; } @@ -342,8 +347,8 @@ public class Keyboard { private int mMaxHeightCount = 0; private int mMaxWidthCount = 0; - private final HashMap<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>(); - private final HashMap<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>(); + private final SparseIntArray mHeightHistogram = new SparseIntArray(); + private final SparseIntArray mWidthHistogram = new SparseIntArray(); private void clearHistogram() { mMostCommonKeyHeight = 0; @@ -355,22 +360,22 @@ public class Keyboard { mWidthHistogram.clear(); } - private static int updateHistogramCounter(HashMap<Integer, Integer> histogram, - Integer key) { - final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1; + private static int updateHistogramCounter(SparseIntArray histogram, int key) { + final int index = histogram.indexOfKey(key); + final int count = (index >= 0 ? histogram.get(key) : 0) + 1; histogram.put(key, count); return count; } private void updateHistogram(Key key) { - final Integer height = key.mHeight + key.mVerticalGap; + final int height = key.mHeight + key.mVerticalGap; final int heightCount = updateHistogramCounter(mHeightHistogram, height); if (heightCount > mMaxHeightCount) { mMaxHeightCount = heightCount; mMostCommonKeyHeight = height; } - final Integer width = key.mWidth + key.mHorizontalGap; + final int width = key.mWidth + key.mHorizontalGap; final int widthCount = updateHistogramCounter(mWidthHistogram, width); if (widthCount > mMaxWidthCount) { mMaxWidthCount = widthCount; @@ -422,67 +427,67 @@ public class Keyboard { * This class parses Keyboard XML file and eventually build a Keyboard. * The Keyboard XML file looks like: * <pre> - * >!-- xml/keyboard.xml --< - * >Keyboard keyboard_attributes*< - * >!-- Keyboard Content --< - * >Row row_attributes*< - * >!-- Row Content --< - * >Key key_attributes* /< - * >Spacer horizontalGap="32.0dp" /< - * >include keyboardLayout="@xml/other_keys"< + * <!-- xml/keyboard.xml --> + * <Keyboard keyboard_attributes*> + * <!-- Keyboard Content --> + * <Row row_attributes*> + * <!-- Row Content --> + * <Key key_attributes* /> + * <Spacer horizontalGap="32.0dp" /> + * <include keyboardLayout="@xml/other_keys"> * ... - * >/Row< - * >include keyboardLayout="@xml/other_rows"< + * </Row> + * <include keyboardLayout="@xml/other_rows"> * ... - * >/Keyboard< + * </Keyboard> * </pre> - * The XML file which is included in other file must have >merge< as root element, + * The XML file which is included in other file must have <merge> as root element, * such as: * <pre> - * >!-- xml/other_keys.xml --< - * >merge< - * >Key key_attributes* /< + * <!-- xml/other_keys.xml --> + * <merge> + * <Key key_attributes* /> * ... - * >/merge< + * </merge> * </pre> * and * <pre> - * >!-- xml/other_rows.xml --< - * >merge< - * >Row row_attributes*< - * >Key key_attributes* /< - * >/Row< + * <!-- xml/other_rows.xml --> + * <merge> + * <Row row_attributes*> + * <Key key_attributes* /> + * </Row> * ... - * >/merge< + * </merge> * </pre> * You can also use switch-case-default tags to select Rows and Keys. * <pre> - * >switch< - * >case case_attribute*< - * >!-- Any valid tags at switch position --< - * >/case< + * <switch> + * <case case_attribute*> + * <!-- Any valid tags at switch position --> + * </case> * ... - * >default< - * >!-- Any valid tags at switch position --< - * >/default< - * >/switch< + * <default> + * <!-- Any valid tags at switch position --> + * </default> + * </switch> * </pre> * You can declare Key style and specify styles within Key tags. * <pre> - * >switch< - * >case mode="email"< - * >key-style styleName="f1-key" parentStyle="modifier-key" + * <switch> + * <case mode="email"> + * <key-style styleName="f1-key" parentStyle="modifier-key" * keyLabel=".com" - * /< - * >/case< - * >case mode="url"< - * >key-style styleName="f1-key" parentStyle="modifier-key" + * /> + * </case> + * <case mode="url"> + * <key-style styleName="f1-key" parentStyle="modifier-key" * keyLabel="http://" - * /< - * >/case< - * >/switch< + * /> + * </case> + * </switch> * ... - * >Key keyStyle="shift-key" ... /< + * <Key keyStyle="shift-key" ... /> * </pre> */ diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 275aacf36..dc27769ab 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -52,6 +52,7 @@ public interface KeyboardActionListener { */ public void onCodeInput(int primaryCode, int x, int y); + // See {@link Adapter#isInvalidCoordinate(int)}. public static final int NOT_A_TOUCH_COORDINATE = -1; public static final int SUGGESTION_STRIP_COORDINATE = -2; public static final int SPELL_CHECKER_COORDINATE = -3; @@ -63,6 +64,20 @@ public interface KeyboardActionListener { */ public void onTextInput(CharSequence text); + // TODO: Should move this method to some more appropriate interface. + /** + * Called when user started batch input. + */ + public void onStartBatchInput(); + + // TODO: Should move this method to some more appropriate interface. + /** + * Sends a sequence of characters to the listener as batch input. + * + * @param text the sequence of characters to be displayed as composing text. + */ + public void onEndBatchInput(CharSequence text); + /** * Called when user released a finger outside any key. */ @@ -84,10 +99,22 @@ public interface KeyboardActionListener { @Override public void onTextInput(CharSequence text) {} @Override + public void onStartBatchInput() {} + @Override + public void onEndBatchInput(CharSequence text) {} + @Override public void onCancelInput() {} @Override public boolean onCustomRequest(int requestCode) { return false; } + + // TODO: Remove this method when the vertical correction is removed. + public static boolean isInvalidCoordinate(int coordinate) { + // Detect {@link KeyboardActionListener#NOT_A_TOUCH_COORDINATE}, + // {@link KeyboardActionListener#SUGGESTION_STRIP_COORDINATE}, and + // {@link KeyboardActionListener#SPELL_CHECKER_COORDINATE}. + return coordinate < 0; + } } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 233716acf..b54c72687 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -137,11 +137,13 @@ public class KeyboardId { } public boolean navigateNext() { - return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0; + return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0 + || imeAction() == EditorInfo.IME_ACTION_NEXT; } public boolean navigatePrevious() { - return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0; + return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0 + || imeAction() == EditorInfo.IME_ACTION_PREVIOUS; } public boolean passwordInput() { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 8c7246855..aab89a3e5 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -29,6 +29,7 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.text.InputType; import android.util.Log; +import android.util.SparseArray; import android.util.Xml; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; @@ -116,9 +117,9 @@ public class KeyboardLayoutSet { InputMethodSubtype mSubtype; int mOrientation; int mWidth; - // KeyboardLayoutSet element id to element's parameters map. - final HashMap<Integer, ElementParams> mKeyboardLayoutSetElementIdToParamsMap = - new HashMap<Integer, ElementParams>(); + // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. + final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap = + new SparseArray<ElementParams>(); static class ElementParams { int mKeyboardXmlId; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 51a0f537f..fb98af3e6 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -30,6 +30,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Message; import android.util.AttributeSet; +import android.util.SparseArray; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; @@ -42,7 +43,6 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.StringUtils; -import java.util.HashMap; import java.util.HashSet; /** @@ -124,12 +124,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private Canvas mCanvas; private final Paint mPaint = new Paint(); private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); - // This map caches key label text height in pixel as value and key label text size as map key. - private static final HashMap<Integer, Float> sTextHeightCache = - new HashMap<Integer, Float>(); - // This map caches key label text width in pixel as value and key label text size as map key. - private static final HashMap<Integer, Float> sTextWidthCache = - new HashMap<Integer, Float>(); + // This sparse array caches key label text height in pixel indexed by key label text size. + private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>(); + // This sparse array caches key label text width in pixel indexed by key label text size. + private static final SparseArray<Float> sTextWidthCache = new SparseArray<Float>(); private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' }; private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' }; @@ -766,7 +764,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private final Rect mTextBounds = new Rect(); private float getCharHeight(char[] referenceChar, Paint paint) { - final Integer key = getCharGeometryCacheKey(referenceChar[0], paint); + final int key = getCharGeometryCacheKey(referenceChar[0], paint); final Float cachedValue = sTextHeightCache.get(key); if (cachedValue != null) return cachedValue; @@ -778,7 +776,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } private float getCharWidth(char[] referenceChar, Paint paint) { - final Integer key = getCharGeometryCacheKey(referenceChar[0], paint); + final int key = getCharGeometryCacheKey(referenceChar[0], paint); final Float cachedValue = sTextWidthCache.get(key); if (cachedValue != null) return cachedValue; @@ -873,6 +871,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0)); } + @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16 @Override public void showKeyPreview(PointerTracker tracker) { if (!mShowKeyPreviewPopup) return; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 383298de9..7714ba892 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -127,7 +127,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke private static final int MSG_TYPING_STATE_EXPIRED = 4; private final KeyTimerParams mParams; - private boolean mInKeyRepeat; public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) { super(outerInstance); @@ -140,8 +139,11 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_REPEAT_KEY: - tracker.onRegisterKey(tracker.getKey()); - startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval); + final Key currentKey = tracker.getKey(); + if (currentKey != null && currentKey.mCode == msg.arg1) { + tracker.onRegisterKey(currentKey); + startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval); + } break; case MSG_LONGPRESS_KEY: if (tracker != null) { @@ -158,22 +160,23 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke } private void startKeyRepeatTimer(PointerTracker tracker, long delay) { - sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay); + final Key key = tracker.getKey(); + if (key == null) return; + sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay); } @Override public void startKeyRepeatTimer(PointerTracker tracker) { - mInKeyRepeat = true; startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout); } public void cancelKeyRepeatTimer() { - mInKeyRepeat = false; removeMessages(MSG_REPEAT_KEY); } + // TODO: Suppress layout changes in key repeat mode public boolean isInKeyRepeat() { - return mInKeyRepeat; + return hasMessages(MSG_REPEAT_KEY); } @Override @@ -451,8 +454,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke */ @Override public void setKeyboard(Keyboard keyboard) { - // Remove any pending messages, except dismissing preview - mKeyTimerHandler.cancelKeyTimers(); + // Remove any pending messages, except dismissing preview and key repeat. + mKeyTimerHandler.cancelLongPressTimer(); super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); @@ -755,15 +758,18 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke final PointerTracker tracker = PointerTracker.getPointerTracker( pointerId, this); final int px, py; + final MotionEvent motionEvent; if (mMoreKeysPanel != null && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) { px = mMoreKeysPanel.translateX((int)me.getX(i)); py = mMoreKeysPanel.translateY((int)me.getY(i)); + motionEvent = null; } else { px = (int)me.getX(i); py = (int)me.getY(i); + motionEvent = me; } - tracker.onMoveEvent(px, py, eventTime); + tracker.onMoveEvent(px, py, eventTime, motionEvent); if (ENABLE_USABILITY_STUDY_LOG) { final float pointerSize = me.getSize(i); final float pointerPressure = me.getPressure(i); diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index be7644fb5..9c8069194 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -58,6 +58,16 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel } @Override + public void onStartBatchInput() { + mListener.onStartBatchInput(); + } + + @Override + public void onEndBatchInput(CharSequence text) { + mListener.onEndBatchInput(text); + } + + @Override public void onCancelInput() { mListener.onCancelInput(); } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index babf6ec99..1ae0020a4 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -148,9 +148,6 @@ public class PointerTracker { // true if this pointer has been long-pressed and is showing a more keys panel. private boolean mIsShowingMoreKeysPanel; - // true if this pointer is repeatable key - private boolean mIsRepeatableKey; - // true if this pointer is in sliding key input boolean mIsInSlidingKeyInput; @@ -242,10 +239,6 @@ public class PointerTracker { + " ignoreModifier=" + ignoreModifierKey + " enabled=" + key.isEnabled()); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(key, - ignoreModifierKey); - } if (ignoreModifierKey) { return false; } @@ -323,6 +316,13 @@ public class PointerTracker { private void setKeyDetectorInner(KeyDetector keyDetector) { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); + final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); + if (newKey != mCurrentKey) { + if (mDrawingProxy != null) { + setReleasedKeyGraphics(mCurrentKey); + } + mCurrentKey = newKey; + } final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4; mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; } @@ -469,7 +469,7 @@ public class PointerTracker { onUpEvent(x, y, eventTime); break; case MotionEvent.ACTION_MOVE: - onMoveEvent(x, y, eventTime); + onMoveEvent(x, y, eventTime, null); break; case MotionEvent.ACTION_CANCEL: onCancelEvent(x, y, eventTime); @@ -525,7 +525,6 @@ public class PointerTracker { || mKeyDetector.alwaysAllowsSlidingInput(); mKeyboardLayoutHasBeenChanged = false; mKeyAlreadyProcessed = false; - mIsRepeatableKey = false; mIsInSlidingKeyInput = false; mIgnoreModifierKey = false; if (key != null) { @@ -549,7 +548,7 @@ public class PointerTracker { mIsInSlidingKeyInput = true; } - public void onMoveEvent(int x, int y, long eventTime) { + public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) { if (DEBUG_MOVE_EVENT) printTouchEvent("onMoveEvent:", x, y, eventTime); if (mKeyAlreadyProcessed) @@ -611,6 +610,15 @@ public class PointerTracker { onUpEventInternal(); onDownEventInternal(x, y, eventTime); } else { + // HACK: If there are currently multiple touches, register the key even if + // the finger slides off the key. This defends against noise from some + // touch panels when there are close multiple touches. + // Caveat: When in chording input mode with a modifier key, we don't use + // this hack. + if (me != null && me.getPointerCount() > 1 + && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { + onUpEventInternal(); + } mKeyAlreadyProcessed = true; setReleasedKeyGraphics(oldKey); } @@ -672,7 +680,7 @@ public class PointerTracker { } if (mKeyAlreadyProcessed) return; - if (!mIsRepeatableKey) { + if (mCurrentKey != null && !mCurrentKey.isRepeatable()) { detectAndSendKey(mCurrentKey, mKeyX, mKeyY); } } @@ -718,9 +726,6 @@ public class PointerTracker { if (key != null && key.isRepeatable()) { onRegisterKey(key); mTimerProxy.startKeyRepeatTimer(this); - mIsRepeatableKey = true; - } else { - mIsRepeatableKey = false; } } @@ -764,14 +769,10 @@ public class PointerTracker { callListenerOnRelease(key, code, false); } - private long mPreviousEventTime; - private void printTouchEvent(String title, int x, int y, long eventTime) { final Key key = mKeyDetector.detectHitKey(x, y); final String code = KeyDetector.printableCode(key); - final long delta = eventTime - mPreviousEventTime; Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title, - (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, code)); - mPreviousEventTime = eventTime; + (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code)); } } diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 1207c3fcd..ae123e29a 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -24,9 +24,10 @@ import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection; import com.android.inputmethod.latin.JniUtils; import java.util.Arrays; -import java.util.HashMap; public class ProximityInfo { + /** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL + * in 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 float SEARCH_DISTANCE = 1.2f; @@ -75,27 +76,6 @@ public class ProximityInfo { mNativeProximityInfo = createNativeProximityInfo(); } - // TODO: Remove this public constructor when the native part of the ProximityInfo becomes - // immutable. - // This public constructor aims only for test purpose. - public ProximityInfo(ProximityInfo o) { - mLocaleStr = o.mLocaleStr; - mGridWidth = o.mGridWidth; - mGridHeight = o.mGridHeight; - mGridSize = o.mGridSize; - mCellWidth = o.mCellWidth; - mCellHeight = o.mCellHeight; - mKeyboardMinWidth = o.mKeyboardMinWidth; - mKeyboardHeight = o.mKeyboardHeight; - mKeyHeight = o.mKeyHeight; - mMostCommonKeyWidth = o.mMostCommonKeyWidth; - mKeys = o.mKeys; - mTouchPositionCorrection = o.mTouchPositionCorrection; - mGridNeighbors = new Key[mGridSize][]; - computeNearestNeighbors(); - mNativeProximityInfo = createNativeProximityInfo(); - } - public static ProximityInfo createDummyProximityInfo() { return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null); } @@ -209,10 +189,6 @@ public class ProximityInfo { private void computeNearestNeighbors() { final int defaultWidth = mMostCommonKeyWidth; final Key[] keys = mKeys; - final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>(); - for (final Key key : keys) { - keyCodeMap.put(key.mCode, key); - } final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE); final int threshold = thresholdBase * thresholdBase; // Round-up so we don't have any pixels outside the grid diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index c4452a5f5..53261205d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -68,12 +68,20 @@ public class KeySpecParser { public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, Locale locale, final KeyboardCodesSet codesSet) { - mCode = toUpperCaseOfCodeForLocale(getCode(moreKeySpec, codesSet), - needsToUpperCase, locale); mLabel = toUpperCaseOfStringForLocale(getLabel(moreKeySpec), needsToUpperCase, locale); - mOutputText = toUpperCaseOfStringForLocale(getOutputText(moreKeySpec), + final int code = toUpperCaseOfCodeForLocale(getCode(moreKeySpec, codesSet), needsToUpperCase, locale); + if (code == Keyboard.CODE_UNSPECIFIED) { + // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters + // upper case representation ("SS"). + mCode = Keyboard.CODE_OUTPUT_TEXT; + mOutputText = mLabel; + } else { + mCode = code; + mOutputText = toUpperCaseOfStringForLocale(getOutputText(moreKeySpec), + needsToUpperCase, locale); + } mIconId = getIconId(moreKeySpec); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 80f4f259b..291b3b943 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.res.TypedArray; import android.util.Log; +import android.util.SparseArray; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; @@ -89,7 +90,7 @@ public class KeyStyles { private class DeclaredKeyStyle extends KeyStyle { private final String mParentStyleName; - private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>(); + private final SparseArray<Object> mStyleAttributes = new SparseArray<Object>(); public DeclaredKeyStyle(String parentStyleName) { mParentStyleName = parentStyleName; @@ -100,8 +101,9 @@ public class KeyStyles { if (a.hasValue(index)) { return parseStringArray(a, index); } - if (mStyleAttributes.containsKey(index)) { - return (String[])mStyleAttributes.get(index); + final Object value = mStyleAttributes.get(index); + if (value != null) { + return (String[])value; } final KeyStyle parentStyle = mStyles.get(mParentStyleName); return parentStyle.getStringArray(a, index); @@ -112,8 +114,9 @@ public class KeyStyles { if (a.hasValue(index)) { return parseString(a, index); } - if (mStyleAttributes.containsKey(index)) { - return (String)mStyleAttributes.get(index); + final Object value = mStyleAttributes.get(index); + if (value != null) { + return (String)value; } final KeyStyle parentStyle = mStyles.get(mParentStyleName); return parentStyle.getString(a, index); @@ -124,8 +127,9 @@ public class KeyStyles { if (a.hasValue(index)) { return a.getInt(index, defaultValue); } - if (mStyleAttributes.containsKey(index)) { - return (Integer)mStyleAttributes.get(index); + final Object value = mStyleAttributes.get(index); + if (value != null) { + return (Integer)value; } final KeyStyle parentStyle = mStyles.get(mParentStyleName); return parentStyle.getInt(a, index, defaultValue); @@ -133,12 +137,13 @@ public class KeyStyles { @Override public int getFlag(TypedArray a, int index) { - int value = a.getInt(index, 0); - if (mStyleAttributes.containsKey(index)) { - value |= (Integer)mStyleAttributes.get(index); + int flags = a.getInt(index, 0); + final Object value = mStyleAttributes.get(index); + if (value != null) { + flags |= (Integer)value; } final KeyStyle parentStyle = mStyles.get(mParentStyleName); - return value | parentStyle.getFlag(a, index); + return flags | parentStyle.getFlag(a, index); } void readKeyAttributes(TypedArray keyAttr) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index 67cb74f4d..f7981a320 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -52,6 +52,7 @@ public class KeyboardCodesSet { "key_action_next", "key_action_previous", "key_language_switch", + "key_research", "key_unspecified", "key_left_parenthesis", "key_right_parenthesis", @@ -86,6 +87,7 @@ public class KeyboardCodesSet { Keyboard.CODE_ACTION_NEXT, Keyboard.CODE_ACTION_PREVIOUS, Keyboard.CODE_LANGUAGE_SWITCH, + Keyboard.CODE_RESEARCH, Keyboard.CODE_UNSPECIFIED, CODE_LEFT_PARENTHESIS, CODE_RIGHT_PARENTHESIS, @@ -112,6 +114,7 @@ public class KeyboardCodesSet { DEFAULT[11], DEFAULT[12], DEFAULT[13], + DEFAULT[14], CODE_RIGHT_PARENTHESIS, CODE_LEFT_PARENTHESIS, CODE_GREATER_THAN_SIGN, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 540e63b3f..5155851fe 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -20,6 +20,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.Log; +import android.util.SparseIntArray; import com.android.inputmethod.latin.R; @@ -31,8 +32,7 @@ public class KeyboardIconsSet { public static final int ICON_UNDEFINED = 0; private static final int ATTR_UNDEFINED = 0; - private static final HashMap<Integer, Integer> ATTR_ID_TO_ICON_ID - = new HashMap<Integer, Integer>(); + private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); // Icon name to icon id map. private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<String, Integer>(); @@ -76,7 +76,9 @@ public class KeyboardIconsSet { } public void loadIcons(final TypedArray keyboardAttrs) { - for (final Integer attrId : ATTR_ID_TO_ICON_ID.keySet()) { + final int size = ATTR_ID_TO_ICON_ID.size(); + for (int index = 0; index < size; index++) { + final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index); try { final Drawable icon = keyboardAttrs.getDrawable(attrId); setDefaultBounds(icon); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 43ffb85f7..4ab6832c3 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -21,8 +21,6 @@ import android.util.Log; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.ResearchLogger; -import com.android.inputmethod.latin.define.ProductionFlag; /** * Keyboard state machine. @@ -305,9 +303,6 @@ public class KeyboardState { Log.d(TAG, "onPressKey: code=" + Keyboard.printableCode(code) + " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.keyboardState_onPressKey(code, this); - } if (code == Keyboard.CODE_SHIFT) { onPressShift(); } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { @@ -341,9 +336,6 @@ public class KeyboardState { Log.d(TAG, "onReleaseKey: code=" + Keyboard.printableCode(code) + " sliding=" + withSliding + " " + this); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.keyboardState_onReleaseKey(this, code, withSliding); - } if (code == Keyboard.CODE_SHIFT) { onReleaseShift(withSliding); } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { @@ -375,9 +367,6 @@ public class KeyboardState { if (DEBUG_EVENT) { Log.d(TAG, "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " " + this); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.keyboardState_onLongPressTimeout(code, this); - } if (mIsAlphabetMode && code == Keyboard.CODE_SHIFT) { mLongPressShiftLockFired = true; mSwitchActions.hapticAndAudioFeedback(code); @@ -509,9 +498,6 @@ public class KeyboardState { if (DEBUG_EVENT) { Log.d(TAG, "onCancelInput: single=" + isSinglePointer + " " + this); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.keyboardState_onCancelInput(isSinglePointer, this); - } // Switch back to the previous keyboard mode if the user cancels sliding input. if (isSinglePointer) { if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) { @@ -543,9 +529,6 @@ public class KeyboardState { + " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.keyboardState_onCodeInput(code, isSinglePointer, autoCaps, this); - } switch (mSwitchState) { case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index 5db65c660..d3bb85d4b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -28,6 +28,7 @@ public class PointerTrackerQueue { private static final String TAG = PointerTrackerQueue.class.getSimpleName(); private static final boolean DEBUG = false; + // TODO: Use ring buffer instead of {@link LinkedList}. private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); public synchronized void add(PointerTracker tracker) { @@ -81,6 +82,20 @@ public class PointerTrackerQueue { } } + public synchronized boolean hasModifierKeyOlderThan(PointerTracker tracker) { + final Iterator<PointerTracker> it = mQueue.iterator(); + while (it.hasNext()) { + final PointerTracker t = it.next(); + if (t == tracker) { + break; + } + if (t.isModifier()) { + return true; + } + } + return false; + } + public synchronized boolean isAnyInSlidingKeyInput() { for (final PointerTracker tracker : mQueue) { if (tracker.isInSlidingKeyInput()) { diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java index e0452483c..c78974dac 100644 --- a/java/src/com/android/inputmethod/latin/AutoCorrection.java +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -21,34 +21,17 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import android.text.TextUtils; import android.util.Log; -import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; public class AutoCorrection { private static final boolean DBG = LatinImeLogger.sDBG; private static final String TAG = AutoCorrection.class.getSimpleName(); + private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; private AutoCorrection() { // Purely static class: can't instantiate. } - public static CharSequence computeAutoCorrectionWord( - final ConcurrentHashMap<String, Dictionary> dictionaries, - final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions, - final CharSequence consideredWord, final float autoCorrectionThreshold, - final CharSequence whitelistedWord) { - if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) { - return whitelistedWord; - } else if (hasAutoCorrectionForConsideredWord( - dictionaries, wordComposer, suggestions, consideredWord)) { - return consideredWord; - } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions, - consideredWord, autoCorrectionThreshold)) { - return suggestions.get(0).mWord; - } - return null; - } - public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries, CharSequence word, boolean ignoreCase) { if (TextUtils.isEmpty(word)) { @@ -56,7 +39,7 @@ public class AutoCorrection { } final CharSequence lowerCasedWord = word.toString().toLowerCase(); for (final String key : dictionaries.keySet()) { - if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue; + if (key.equals(Dictionary.TYPE_WHITELIST)) continue; final Dictionary dictionary = dictionaries.get(key); // It's unclear how realistically 'dictionary' can be null, but the monkey is somehow // managing to get null in here. Presumably the language is changing to a language with @@ -81,7 +64,7 @@ public class AutoCorrection { } int maxFreq = -1; for (final String key : dictionaries.keySet()) { - if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue; + if (key.equals(Dictionary.TYPE_WHITELIST)) continue; final Dictionary dictionary = dictionaries.get(key); if (null == dictionary) continue; final int tempFreq = dictionary.getFrequency(word); @@ -92,11 +75,12 @@ public class AutoCorrection { return maxFreq; } - public static boolean allowsToBeAutoCorrected( + // Returns true if this is a whitelist entry, or it isn't in any dictionary. + public static boolean isWhitelistedOrNotAWord( final ConcurrentHashMap<String, Dictionary> dictionaries, final CharSequence word, final boolean ignoreCase) { final WhitelistDictionary whitelistDictionary = - (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST); + (WhitelistDictionary)dictionaries.get(Dictionary.TYPE_WHITELIST); // If "word" is in the whitelist dictionary, it should not be auto corrected. if (whitelistDictionary != null && whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) { @@ -105,33 +89,18 @@ public class AutoCorrection { return !isValidWord(dictionaries, word, ignoreCase); } - private static boolean hasAutoCorrectionForWhitelistedWord(CharSequence whiteListedWord) { - return whiteListedWord != null; - } - - private static boolean hasAutoCorrectionForConsideredWord( - final ConcurrentHashMap<String, Dictionary> dictionaries, - final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions, - final CharSequence consideredWord) { - if (TextUtils.isEmpty(consideredWord)) return false; - return wordComposer.size() > 1 && suggestions.size() > 0 - && !allowsToBeAutoCorrected(dictionaries, consideredWord, false); - } - - private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer, - ArrayList<SuggestedWordInfo> suggestions, + public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion, CharSequence consideredWord, float autoCorrectionThreshold) { - if (wordComposer.size() > 1 && suggestions.size() > 0) { - final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0); + if (null != suggestion) { //final int autoCorrectionSuggestionScore = sortedScores[0]; - final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.mScore; + final int autoCorrectionSuggestionScore = suggestion.mScore; // TODO: when the normalized score of the first suggestion is nearly equals to // the normalized score of the second suggestion, behave less aggressive. final float normalizedScore = BinaryDictionary.calcNormalizedScore( - consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(), + consideredWord.toString(), suggestion.mWord.toString(), autoCorrectionSuggestionScore); if (DBG) { - Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + "," + Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + "," + autoCorrectionSuggestionScore + ", " + normalizedScore + "(" + autoCorrectionThreshold + ")"); } @@ -139,10 +108,43 @@ public class AutoCorrection { if (DBG) { Log.d(TAG, "Auto corrected by S-threshold."); } - return true; + return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(), + suggestion.mWord); } } return false; } + // TODO: Resolve the inconsistencies between the native auto correction algorithms and + // this safety net + public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord, + final CharSequence suggestion) { + // Safety net for auto correction. + // Actually if we hit this safety net, it's a bug. + // If user selected aggressive auto correction mode, there is no need to use the safety + // net. + // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH, + // we should not use net because relatively edit distance can be big. + final int typedWordLength = typedWord.length(); + if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) { + return false; + } + final int maxEditDistanceOfNativeDictionary = + (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1; + final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString()); + if (DBG) { + Log.d(TAG, "Autocorrected edit distance = " + distance + + ", " + maxEditDistanceOfNativeDictionary); + } + if (distance > maxEditDistanceOfNativeDictionary) { + if (DBG) { + Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion); + Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. " + + "Turning off auto-correction."); + } + return true; + } else { + return false; + } + } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index d0613bd72..291d84975 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -20,7 +20,9 @@ import android.content.Context; import android.text.TextUtils; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; @@ -40,19 +42,19 @@ public class BinaryDictionary extends Dictionary { */ public static final int MAX_WORD_LENGTH = 48; public static final int MAX_WORDS = 18; + public static final int MAX_SPACES = 16; private static final String TAG = "BinaryDictionary"; private static final int MAX_BIGRAMS = 60; + private static final int MAX_RESULTS = MAX_BIGRAMS > MAX_WORDS ? MAX_BIGRAMS : MAX_WORDS; private static final int TYPED_LETTER_MULTIPLIER = 2; - private int mDicTypeId; private long mNativeDict; private final int[] mInputCodes = new int[MAX_WORD_LENGTH]; - private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS]; - private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS]; - private final int[] mScores = new int[MAX_WORDS]; - private final int[] mBigramScores = new int[MAX_BIGRAMS]; + private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS]; + private final int[] mSpaceIndices = new int[MAX_SPACES]; + private final int[] mOutputScores = new int[MAX_RESULTS]; private final boolean mUseFullEditDistance; @@ -65,14 +67,12 @@ public class BinaryDictionary extends Dictionary { * @param offset the offset of the dictionary data within the file. * @param length the length of the binary data. * @param useFullEditDistance whether to use the full edit distance in suggestions + * @param dictType the dictionary type, as a human-readable string */ public BinaryDictionary(final Context context, final String filename, final long offset, final long length, - final boolean useFullEditDistance, final Locale locale) { - // Note: at the moment a binary dictionary is always of the "main" type. - // Initializing this here will help transitioning out of the scheme where - // the Suggest class knows everything about every single dictionary. - mDicTypeId = Suggest.DIC_MAIN; + final boolean useFullEditDistance, final Locale locale, final String dictType) { + super(dictType); mUseFullEditDistance = useFullEditDistance; loadDictionary(filename, offset, length); } @@ -87,8 +87,10 @@ public class BinaryDictionary extends Dictionary { private native int getFrequencyNative(long dict, int[] word, int wordLength); private native boolean isValidBigramNative(long dict, int[] word1, int[] word2); private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates, - int[] yCoordinates, int[] inputCodes, int codesSize, int[] prevWordForBigrams, - boolean useFullEditDistance, char[] outputChars, int[] scores); + int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, int codesSize, + int commitPoint, boolean isGesture, + int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars, + int[] scores, int[] outputIndices); private native int getBigramsNative(long dict, int[] prevWord, int prevWordLength, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, int maxWordLength, int maxBigrams); @@ -103,13 +105,23 @@ public class BinaryDictionary extends Dictionary { } @Override - public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback) { - if (mNativeDict == 0) return; + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { + if (composer.size() <= 1) { + return TextUtils.isEmpty(prevWord) ? null : getBigramsInternal(composer, prevWord); + } else { + return getWordsInternal(composer, prevWord, proximityInfo); + } + } + + // TODO: move to native code + private ArrayList<SuggestedWordInfo> getBigramsInternal(final WordComposer codes, + final CharSequence previousWord) { + if (!isValidDictionary()) return null; int[] codePoints = StringUtils.toCodePointArray(previousWord.toString()); - Arrays.fill(mOutputChars_bigrams, (char) 0); - Arrays.fill(mBigramScores, 0); + Arrays.fill(mOutputChars, (char) 0); + Arrays.fill(mOutputScores, 0); int codesSize = codes.size(); Arrays.fill(mInputCodes, -1); @@ -118,44 +130,53 @@ public class BinaryDictionary extends Dictionary { } int count = getBigramsNative(mNativeDict, codePoints, codePoints.length, mInputCodes, - codesSize, mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS); + codesSize, mOutputChars, mOutputScores, MAX_WORD_LENGTH, MAX_BIGRAMS); if (count > MAX_BIGRAMS) { count = MAX_BIGRAMS; } + final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); for (int j = 0; j < count; ++j) { - if (codesSize > 0 && mBigramScores[j] < 1) break; + if (codesSize > 0 && mOutputScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; int len = 0; - while (len < MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) { + while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { ++len; } if (len > 0) { - callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j], - mDicTypeId, Dictionary.BIGRAM); + suggestions.add(new SuggestedWordInfo( + new String(mOutputChars, start, len), + mOutputScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType)); } } + return suggestions; } + // TODO: move to native code // proximityInfo and/or prevWordForBigrams may not be null. - @Override - public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams, - final WordCallback callback, final ProximityInfo proximityInfo) { + private ArrayList<SuggestedWordInfo> getWordsInternal(final WordComposer codes, + final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { + if (!isValidDictionary()) return null; + final int count = getSuggestions(codes, prevWordForBigrams, proximityInfo, mOutputChars, - mScores); + mOutputScores, mSpaceIndices); + final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); for (int j = 0; j < count; ++j) { - if (mScores[j] < 1) break; + if (mOutputScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; int len = 0; while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { ++len; } if (len > 0) { - callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId, - Dictionary.UNIGRAM); + // TODO: actually get the kind from native code + suggestions.add(new SuggestedWordInfo( + new String(mOutputChars, start, len), + mOutputScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType)); } } + return suggestions; } /* package for test */ boolean isValidDictionary() { @@ -163,30 +184,35 @@ public class BinaryDictionary extends Dictionary { } // proximityInfo may not be null. - /* package for test */ int getSuggestions(final WordComposer codes, + private int getSuggestions(final WordComposer codes, final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo, - char[] outputChars, int[] scores) { - if (!isValidDictionary()) return -1; - - final int codesSize = codes.size(); - // Won't deal with really long words. - if (codesSize > MAX_WORD_LENGTH - 1) return -1; - + char[] outputChars, int[] scores, int[] spaceIndices) { Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE); - for (int i = 0; i < codesSize; i++) { - mInputCodes[i] = codes.getCodeAt(i); - } Arrays.fill(outputChars, (char) 0); Arrays.fill(scores, 0); - final int[] prevWordCodePointArray = null == prevWordForBigrams + final InputPointers ips = codes.getInputPointers(); + final boolean isGesture = codes.isBatchMode(); + final int codesSize; + if (isGesture) { + codesSize = ips.getPointerSize(); + } else { + codesSize = codes.size(); + // Won't deal with really long words. + if (codesSize > MAX_WORD_LENGTH - 1) return -1; + for (int i = 0; i < codesSize; i++) { + mInputCodes[i] = codes.getCodeAt(i); + } + } + + // TODO: toLowerCase in the native code + final int[] prevWordCodePointArray = (null == prevWordForBigrams) ? null : StringUtils.toCodePointArray(prevWordForBigrams.toString()); - // TODO: pass the previous word to native code - return getSuggestionsNative( - mNativeDict, proximityInfo.getNativeProximityInfo(), - codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, - prevWordCodePointArray, mUseFullEditDistance, outputChars, scores); + return getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), + ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), + mInputCodes, codesSize, 0 /* unused */, isGesture, prevWordCodePointArray, + mUseFullEditDistance, outputChars, scores, spaceIndices); } public static float calcNormalizedScore(String before, String after, int score) { diff --git a/java/src/com/android/inputmethod/latin/BoundedTreeSet.java b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java new file mode 100644 index 000000000..cf977617d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + +import java.util.Collection; +import java.util.Comparator; +import java.util.TreeSet; + +/** + * A TreeSet that is bounded in size and throws everything that's smaller than its limit + */ +public class BoundedTreeSet extends TreeSet<SuggestedWordInfo> { + private final int mCapacity; + public BoundedTreeSet(final Comparator<SuggestedWordInfo> comparator, final int capacity) { + super(comparator); + mCapacity = capacity; + } + + @Override + public boolean add(final SuggestedWordInfo e) { + if (size() < mCapacity) return super.add(e); + if (comparator().compare(e, last()) > 0) return false; + super.add(e); + pollLast(); // removes the last element + return true; + } + + @Override + public boolean addAll(final Collection<? extends SuggestedWordInfo> e) { + if (null == e) return false; + return super.addAll(e); + } +} diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 34308dfb3..5edc4314f 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -52,6 +52,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { /** The number of contacts in the most recent dictionary rebuild. */ static private int sContactCountAtLastRebuild = 0; + /** The locale for this contacts dictionary. Controls name bigram predictions. */ + public final Locale mLocale; + private ContentObserver mObserver; /** @@ -59,8 +62,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { */ private final boolean mUseFirstLastBigrams; - public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) { - super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId); + public ContactsBinaryDictionary(final Context context, Locale locale) { + super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS); + mLocale = locale; mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); registerObserver(context); @@ -116,12 +120,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } } - @Override - public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback) { - super.getBigrams(codes, previousWord, callback); - } - private boolean useFirstLastBigramsForLocale(Locale locale) { // TODO: Add firstname/lastname bigram rules for other languages. if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { @@ -163,7 +161,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { * bigrams depending on locale. */ private void addName(String name) { - int len = name.codePointCount(0, name.length()); + int len = StringUtils.codePointCount(name); String prevWord = null; // TODO: Better tokenization for non-Latin writing systems for (int i = 0; i < len; i++) { @@ -173,7 +171,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { i = end - 1; // Don't add single letter words, possibly confuses // capitalization of i. - final int wordLen = word.codePointCount(0, word.length()); + final int wordLen = StringUtils.codePointCount(word); if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS); if (!TextUtils.isEmpty(prevWord)) { @@ -262,14 +260,14 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { * Checks if the words in a name are in the current binary dictionary. */ private boolean isNameInDictionary(String name) { - int len = name.codePointCount(0, name.length()); + int len = StringUtils.codePointCount(name); String prevWord = null; for (int i = 0; i < len; i++) { if (Character.isLetter(name.codePointAt(i))) { int end = getWordEndPosition(name, len, i); String word = name.substring(i, end); i = end - 1; - final int wordLen = word.codePointCount(0, word.length()); + final int wordLen = StringUtils.codePointCount(word); if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) { if (!super.isValidBigramLocked(prevWord, word)) { diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java deleted file mode 100644 index cbfbd0ec8..000000000 --- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.database.Cursor; -import android.os.SystemClock; -import android.provider.BaseColumns; -import android.provider.ContactsContract.Contacts; -import android.text.TextUtils; -import android.util.Log; - -import com.android.inputmethod.keyboard.Keyboard; - -// TODO: This class is superseded by {@link ContactsBinaryDictionary}. Should be cleaned up. -/** - * An expandable dictionary that stores the words from Contacts provider. - * - * @deprecated Use {@link ContactsBinaryDictionary}. - */ -@Deprecated -public class ContactsDictionary extends ExpandableDictionary { - - private static final String[] PROJECTION = { - BaseColumns._ID, - Contacts.DISPLAY_NAME, - }; - - private static final String TAG = "ContactsDictionary"; - - /** - * Frequency for contacts information into the dictionary - */ - private static final int FREQUENCY_FOR_CONTACTS = 40; - private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90; - - private static final int INDEX_NAME = 1; - - private ContentObserver mObserver; - - private long mLastLoadedContacts; - - public ContactsDictionary(final Context context, final int dicTypeId) { - super(context, dicTypeId); - registerObserver(context); - loadDictionary(); - } - - private synchronized void registerObserver(final Context context) { - // Perform a managed query. The Activity will handle closing and requerying the cursor - // when needed. - if (mObserver != null) return; - ContentResolver cres = context.getContentResolver(); - cres.registerContentObserver( - Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) { - @Override - public void onChange(boolean self) { - setRequiresReload(true); - } - }); - } - - public void reopen(final Context context) { - registerObserver(context); - } - - @Override - public synchronized void close() { - if (mObserver != null) { - getContext().getContentResolver().unregisterContentObserver(mObserver); - mObserver = null; - } - super.close(); - } - - @Override - public void startDictionaryLoadingTaskLocked() { - long now = SystemClock.uptimeMillis(); - if (mLastLoadedContacts == 0 - || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) { - super.startDictionaryLoadingTaskLocked(); - } - } - - @Override - public void loadDictionaryAsync() { - try { - Cursor cursor = getContext().getContentResolver() - .query(Contacts.CONTENT_URI, PROJECTION, null, null, null); - if (cursor != null) { - addWords(cursor); - } - } catch(IllegalStateException e) { - Log.e(TAG, "Contacts DB is having problems"); - } - mLastLoadedContacts = SystemClock.uptimeMillis(); - } - - @Override - public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback) { - // Do not return bigrams from Contacts when nothing was typed. - if (codes.size() <= 0) return; - super.getBigrams(codes, previousWord, callback); - } - - private void addWords(Cursor cursor) { - clearDictionary(); - - final int maxWordLength = getMaxWordLength(); - try { - if (cursor.moveToFirst()) { - while (!cursor.isAfterLast()) { - String name = cursor.getString(INDEX_NAME); - - if (name != null && -1 == name.indexOf('@')) { - int len = name.length(); - String prevWord = null; - - // TODO: Better tokenization for non-Latin writing systems - for (int i = 0; i < len; i++) { - if (Character.isLetter(name.charAt(i))) { - int j; - for (j = i + 1; j < len; j++) { - char c = name.charAt(j); - - if (!(c == Keyboard.CODE_DASH - || c == Keyboard.CODE_SINGLE_QUOTE - || Character.isLetter(c))) { - break; - } - } - - String word = name.substring(i, j); - i = j - 1; - - // Safeguard against adding really long words. Stack - // may overflow due to recursion - // Also don't add single letter words, possibly confuses - // capitalization of i. - final int wordLen = word.length(); - if (wordLen < maxWordLength && wordLen > 1) { - super.addWord(word, null /* shortcut */, - FREQUENCY_FOR_CONTACTS); - if (!TextUtils.isEmpty(prevWord)) { - super.setBigramAndGetFrequency(prevWord, word, - FREQUENCY_FOR_CONTACTS_BIGRAM); - } - prevWord = word; - } - } - } - } - cursor.moveToNext(); - } - } - cursor.close(); - } catch(IllegalStateException e) { - Log.e(TAG, "Contacts DB is having problems"); - } - } -} diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 7cd9bc2a8..fd40aa6da 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -16,7 +16,12 @@ package com.android.inputmethod.latin; +import android.text.TextUtils; + import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + +import java.util.ArrayList; /** * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key @@ -28,55 +33,36 @@ public abstract class Dictionary { */ protected static final int FULL_WORD_SCORE_MULTIPLIER = 2; - public static final int UNIGRAM = 0; - public static final int BIGRAM = 1; - public static final int NOT_A_PROBABILITY = -1; - /** - * Interface to be implemented by classes requesting words to be fetched from the dictionary. - * @see #getWords(WordComposer, CharSequence, WordCallback, ProximityInfo) - */ - public interface WordCallback { - /** - * Adds a word to a list of suggestions. The word is expected to be ordered based on - * the provided score. - * @param word the character array containing the word - * @param wordOffset starting offset of the word in the character array - * @param wordLength length of valid characters in the character array - * @param score the score of occurrence. This is normalized between 1 and 255, but - * can exceed those limits - * @param dicTypeId of the dictionary where word was from - * @param dataType tells type of this data, either UNIGRAM or BIGRAM - * @return true if the word was added, false if no more words are required - */ - boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId, - int dataType); + + public static final String TYPE_USER_TYPED = "user_typed"; + public static final String TYPE_APPLICATION_DEFINED = "application_defined"; + public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such + public static final String TYPE_MAIN = "main"; + public static final String TYPE_CONTACTS = "contacts"; + // User dictionary, the system-managed one. + public static final String TYPE_USER = "user"; + // User history dictionary internal to LatinIME. + public static final String TYPE_USER_HISTORY = "history"; + public static final String TYPE_WHITELIST ="whitelist"; + protected final String mDictType; + + public Dictionary(final String dictType) { + mDictType = dictType; } /** - * Searches for words in the dictionary that match the characters in the composer. Matched - * words are added through the callback object. - * @param composer the key sequence to match - * @param prevWordForBigrams the previous word, or null if none - * @param callback the callback object to send matched words to as possible candidates + * Searches for suggestions for a given context. For the moment the context is only the + * previous word. + * @param composer the key sequence to match with coordinate info, as a WordComposer + * @param prevWord the previous word, or null if none * @param proximityInfo the object for key proximity. May be ignored by some implementations. - * @see WordCallback#addWord(char[], int, int, int, int, int) - */ - abstract public void getWords(final WordComposer composer, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo); - - /** - * Searches for pairs in the bigram dictionary that matches the previous word and all the - * possible words following are added through the callback object. - * @param composer the key sequence to match - * @param previousWord the word before - * @param callback the callback object to send possible word following previous word + * @return the list of suggestions (possibly null if none) */ - public void getBigrams(final WordComposer composer, final CharSequence previousWord, - final WordCallback callback) { - // empty base implementation - } + // TODO: pass more context than just the previous word, to enable better suggestions (n-gram + // and more) + abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo); /** * Checks if the given word occurs in the dictionary @@ -115,4 +101,12 @@ public abstract class Dictionary { public void close() { // empty base implementation } + + /** + * Subclasses may override to indicate that this Dictionary is not yet properly initialized. + */ + + public boolean isInitialized() { + return true; + } } diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 1a05fcd86..88ac07d7a 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -17,9 +17,11 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import android.util.Log; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CopyOnWriteArrayList; @@ -31,11 +33,13 @@ public class DictionaryCollection extends Dictionary { private final String TAG = DictionaryCollection.class.getSimpleName(); protected final CopyOnWriteArrayList<Dictionary> mDictionaries; - public DictionaryCollection() { + public DictionaryCollection(final String dictType) { + super(dictType); mDictionaries = new CopyOnWriteArrayList<Dictionary>(); } - public DictionaryCollection(Dictionary... dictionaries) { + public DictionaryCollection(final String dictType, Dictionary... dictionaries) { + super(dictType); if (null == dictionaries) { mDictionaries = new CopyOnWriteArrayList<Dictionary>(); } else { @@ -44,23 +48,29 @@ public class DictionaryCollection extends Dictionary { } } - public DictionaryCollection(Collection<Dictionary> dictionaries) { + public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) { + super(dictType); mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } @Override - public void getWords(final WordComposer composer, final CharSequence prevWordForBigrams, - final WordCallback callback, final ProximityInfo proximityInfo) { - for (final Dictionary dict : mDictionaries) - dict.getWords(composer, prevWordForBigrams, callback, proximityInfo); - } - - @Override - public void getBigrams(final WordComposer composer, final CharSequence previousWord, - final WordCallback callback) { - for (final Dictionary dict : mDictionaries) - dict.getBigrams(composer, previousWord, callback); + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { + final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries; + if (dictionaries.isEmpty()) return null; + // To avoid creating unnecessary objects, we get the list out of the first + // dictionary and add the rest to it if not null, hence the get(0) + ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer, + prevWord, proximityInfo); + if (null == suggestions) suggestions = new ArrayList<SuggestedWordInfo>(); + final int length = dictionaries.size(); + for (int i = 0; i < length; ++ i) { + final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer, + prevWord, proximityInfo); + if (null != sugg) suggestions.addAll(sugg); + } + return suggestions; } @Override @@ -82,8 +92,9 @@ public class DictionaryCollection extends Dictionary { return maxFreq; } - public boolean isEmpty() { - return mDictionaries.isEmpty(); + @Override + public boolean isInitialized() { + return !mDictionaries.isEmpty(); } @Override diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index a22d73af7..06a5f4b72 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -49,7 +49,8 @@ public class DictionaryFactory { final Locale locale, final boolean useFullEditDistance) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); - return new DictionaryCollection(createBinaryDictionary(context, locale)); + return new DictionaryCollection(Dictionary.TYPE_MAIN, + createBinaryDictionary(context, locale)); } final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>(); @@ -59,7 +60,7 @@ public class DictionaryFactory { for (final AssetFileAddress f : assetFileList) { final BinaryDictionary binaryDictionary = new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, - useFullEditDistance, locale); + useFullEditDistance, locale, Dictionary.TYPE_MAIN); if (binaryDictionary.isValidDictionary()) { dictList.add(binaryDictionary); } @@ -69,7 +70,7 @@ public class DictionaryFactory { // If the list is empty, that means we should not use any dictionary (for example, the user // explicitly disabled the main dictionary), so the following is okay. dictList is never // null, but if for some reason it is, DictionaryCollection handles it gracefully. - return new DictionaryCollection(dictList); + return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList); } /** @@ -112,7 +113,7 @@ public class DictionaryFactory { return null; } return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(), - false /* useFullEditDistance */, locale); + false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN); } catch (android.content.res.Resources.NotFoundException e) { Log.e(TAG, "Could not find the resource"); return null; @@ -140,7 +141,7 @@ public class DictionaryFactory { long startOffset, long length, final boolean useFullEditDistance, Locale locale) { if (dictionary.isFile()) { return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length, - useFullEditDistance, locale); + useFullEditDistance, locale, Dictionary.TYPE_MAIN); } else { Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); return null; diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java deleted file mode 100644 index 0f34d50bb..000000000 --- a/java/src/com/android/inputmethod/latin/EditingUtils.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.latin; - -import android.view.inputmethod.ExtractedText; -import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputConnection; - -import java.util.regex.Pattern; - -/** - * Utility methods to deal with editing text through an InputConnection. - */ -public class EditingUtils { - /** - * Number of characters we want to look back in order to identify the previous word - */ - // Provision for a long word pair and a separator - private static final int LOOKBACK_CHARACTER_NUM = BinaryDictionary.MAX_WORD_LENGTH * 2 + 1; - private static final int INVALID_CURSOR_POSITION = -1; - - private EditingUtils() { - // Unintentional empty constructor for singleton. - } - - private static int getCursorPosition(InputConnection connection) { - if (null == connection) return INVALID_CURSOR_POSITION; - final ExtractedText extracted = connection.getExtractedText(new ExtractedTextRequest(), 0); - if (extracted == null) { - return INVALID_CURSOR_POSITION; - } - return extracted.startOffset + extracted.selectionStart; - } - - /** - * @param connection connection to the current text field. - * @param separators characters which may separate words - * @return the word that surrounds the cursor, including up to one trailing - * separator. For example, if the field contains "he|llo world", where | - * represents the cursor, then "hello " will be returned. - */ - public static String getWordAtCursor(InputConnection connection, String separators) { - // getWordRangeAtCursor returns null if the connection is null - Range r = getWordRangeAtCursor(connection, separators); - return (r == null) ? null : r.mWord; - } - - /** - * Represents a range of text, relative to the current cursor position. - */ - public static class Range { - /** Characters before selection start */ - public final int mCharsBefore; - - /** - * Characters after selection start, including one trailing word - * separator. - */ - public final int mCharsAfter; - - /** The actual characters that make up a word */ - public final String mWord; - - public Range(int charsBefore, int charsAfter, String word) { - if (charsBefore < 0 || charsAfter < 0) { - throw new IndexOutOfBoundsException(); - } - this.mCharsBefore = charsBefore; - this.mCharsAfter = charsAfter; - this.mWord = word; - } - } - - private static Range getWordRangeAtCursor(InputConnection connection, String sep) { - if (connection == null || sep == null) { - return null; - } - CharSequence before = connection.getTextBeforeCursor(1000, 0); - CharSequence after = connection.getTextAfterCursor(1000, 0); - if (before == null || after == null) { - return null; - } - - // Find first word separator before the cursor - int start = before.length(); - while (start > 0 && !isWhitespace(before.charAt(start - 1), sep)) start--; - - // Find last word separator after the cursor - int end = -1; - while (++end < after.length() && !isWhitespace(after.charAt(end), sep)) { - // Nothing to do here. - } - - int cursor = getCursorPosition(connection); - if (start >= 0 && cursor + end <= after.length() + before.length()) { - String word = before.toString().substring(start, before.length()) - + after.toString().substring(0, end); - return new Range(before.length() - start, end, word); - } - - return null; - } - - private static boolean isWhitespace(int code, String whitespace) { - return whitespace.contains(String.valueOf((char) code)); - } - - private static final Pattern spaceRegex = Pattern.compile("\\s+"); - - public static CharSequence getPreviousWord(InputConnection connection, - String sentenceSeperators) { - //TODO: Should fix this. This could be slow! - if (null == connection) return null; - CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); - return getPreviousWord(prev, sentenceSeperators); - } - - // Get the word before the whitespace preceding the non-whitespace preceding the cursor. - // Also, it won't return words that end in a separator. - // Example : - // "abc def|" -> abc - // "abc def |" -> abc - // "abc def. |" -> abc - // "abc def . |" -> def - // "abc|" -> null - // "abc |" -> null - // "abc. def|" -> null - public static CharSequence getPreviousWord(CharSequence prev, String sentenceSeperators) { - if (prev == null) return null; - String[] w = spaceRegex.split(prev); - - // If we can't find two words, or we found an empty word, return null. - if (w.length < 2 || w[w.length - 2].length() <= 0) return null; - - // If ends in a separator, return null - char lastChar = w[w.length - 2].charAt(w[w.length - 2].length() - 1); - if (sentenceSeperators.contains(String.valueOf(lastChar))) return null; - - return w[w.length - 2]; - } - - public static CharSequence getThisWord(InputConnection connection, String sentenceSeperators) { - if (null == connection) return null; - final CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); - return getThisWord(prev, sentenceSeperators); - } - - // Get the word immediately before the cursor, even if there is whitespace between it and - // the cursor - but not if there is punctuation. - // Example : - // "abc def|" -> def - // "abc def |" -> def - // "abc def. |" -> null - // "abc def . |" -> null - public static CharSequence getThisWord(CharSequence prev, String sentenceSeperators) { - if (prev == null) return null; - String[] w = spaceRegex.split(prev); - - // No word : return null - if (w.length < 1 || w[w.length - 1].length() <= 0) return null; - - // If ends in a separator, return null - char lastChar = w[w.length - 1].charAt(w[w.length - 1].length() - 1); - if (sentenceSeperators.contains(String.valueOf(lastChar))) return null; - - return w[w.length - 1]; - } -} diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index c65404cbc..016530abb 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -19,6 +19,7 @@ import android.os.SystemClock; import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.Node; @@ -75,9 +76,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** The expandable fusion dictionary used to generate the binary dictionary. */ private FusionDictionary mFusionDictionary; - /** The dictionary type id. */ - public final int mDicTypeId; - /** * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple * dictionary instances with the same filename is supported, with access controlled by @@ -123,11 +121,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * @param context The application context of the parent. * @param filename The filename for this binary dictionary. Multiple dictionaries with the same * filename is supported. - * @param dictType The type of this dictionary. + * @param dictType the dictionary type, as a human-readable string */ public ExpandableBinaryDictionary( - final Context context, final String filename, final int dictType) { - mDicTypeId = dictType; + final Context context, final String filename, final String dictType) { + super(dictType); mFilename = filename; mContext = context; mBinaryDictionary = null; @@ -194,46 +192,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams, - final WordCallback callback, final ProximityInfo proximityInfo) { + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { asyncReloadDictionaryIfRequired(); - getWordsInner(codes, prevWordForBigrams, callback, proximityInfo); - } - - protected final void getWordsInner(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { - // Ensure that there are no concurrent calls to getWords. If there are, do nothing and - // return. - if (mLocalDictionaryController.tryLock()) { - try { - if (mBinaryDictionary != null) { - mBinaryDictionary.getWords(codes, prevWordForBigrams, callback, proximityInfo); - } - } finally { - mLocalDictionaryController.unlock(); - } - } - } - - @Override - public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback) { - asyncReloadDictionaryIfRequired(); - getBigramsInner(codes, previousWord, callback); - } - - protected void getBigramsInner(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback) { if (mLocalDictionaryController.tryLock()) { try { if (mBinaryDictionary != null) { - mBinaryDictionary.getBigrams(codes, previousWord, callback); + return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo); } } finally { mLocalDictionaryController.unlock(); } } + return null; } @Override @@ -306,7 +277,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // Build the new binary dictionary final BinaryDictionary newBinaryDictionary = new BinaryDictionary(mContext, filename, 0, length, true /* useFullEditDistance */, - null); + null, mDictType); if (mBinaryDictionary != null) { // Ensure all threads accessing the current dictionary have finished before swapping in diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 34a92fd30..5d7995dc2 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -17,10 +17,12 @@ package com.android.inputmethod.latin; import android.content.Context; +import android.text.TextUtils; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams; import java.util.ArrayList; @@ -37,7 +39,6 @@ public class ExpandableDictionary extends Dictionary { private Context mContext; private char[] mWordBuilder = new char[BinaryDictionary.MAX_WORD_LENGTH]; - private int mDicTypeId; private int mMaxDepth; private int mInputLength; @@ -151,11 +152,11 @@ public class ExpandableDictionary extends Dictionary { private int[][] mCodes; - public ExpandableDictionary(Context context, int dicTypeId) { + public ExpandableDictionary(final Context context, final String dictType) { + super(dictType); mContext = context; clearDictionary(); mCodes = new int[BinaryDictionary.MAX_WORD_LENGTH][]; - mDicTypeId = dicTypeId; } public void loadDictionary() { @@ -247,27 +248,43 @@ public class ExpandableDictionary extends Dictionary { } @Override - public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams, - final WordCallback callback, final ProximityInfo proximityInfo) { + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { + if (reloadDictionaryIfRequired()) return null; + if (composer.size() <= 1) { + if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) { + return null; + } + final ArrayList<SuggestedWordInfo> suggestions = + getWordsInner(composer, prevWord, proximityInfo); + return suggestions; + } else { + if (TextUtils.isEmpty(prevWord)) return null; + final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); + runBigramReverseLookUp(prevWord, suggestions); + return suggestions; + } + } + + // This reloads the dictionary if required, and returns whether it's currently updating its + // contents or not. + // @VisibleForTesting + boolean reloadDictionaryIfRequired() { synchronized (mUpdatingLock) { // If we need to update, start off a background task if (mRequiresReload) startDictionaryLoadingTaskLocked(); - // Currently updating contacts, don't return any results. - if (mUpdatingDictionary) return; - } - if (codes.size() >= BinaryDictionary.MAX_WORD_LENGTH) { - return; + return mUpdatingDictionary; } - getWordsInner(codes, prevWordForBigrams, callback, proximityInfo); } - protected final void getWordsInner(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { + protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes, + final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { + final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); mInputLength = codes.size(); if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; - final int[] xCoordinates = codes.getXCoordinates(); - final int[] yCoordinates = codes.getYCoordinates(); + final InputPointers ips = codes.getInputPointers(); + final int[] xCoordinates = ips.getXCoordinates(); + final int[] yCoordinates = ips.getYCoordinates(); // Cache the codes so that we don't have to lookup an array list for (int i = 0; i < mInputLength; i++) { // TODO: Calculate proximity info here. @@ -281,10 +298,11 @@ public class ExpandableDictionary extends Dictionary { proximityInfo.fillArrayWithNearestKeyCodes(x, y, codes.getCodeAt(i), mCodes[i]); } mMaxDepth = mInputLength * 3; - getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, callback); + getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, suggestions); for (int i = 0; i < mInputLength; i++) { - getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, callback); + getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, suggestions); } + return suggestions; } @Override @@ -368,24 +386,27 @@ public class ExpandableDictionary extends Dictionary { * @param word the word to insert, as an array of code points * @param depth the depth of the node in the tree * @param finalFreq the frequency for this word + * @param suggestions the suggestion collection to add the suggestions to * @return whether there is still space for more words. - * @see Dictionary.WordCallback#addWord(char[], int, int, int, int, int) */ private boolean addWordAndShortcutsFromNode(final Node node, final char[] word, final int depth, - final int finalFreq, final WordCallback callback) { + final int finalFreq, final ArrayList<SuggestedWordInfo> suggestions) { if (finalFreq > 0 && !node.mShortcutOnly) { - if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId, Dictionary.UNIGRAM)) { - return false; - } + // Use KIND_CORRECTION always. This dictionary does not really have a notion of + // COMPLETION against CORRECTION; we could artificially add one by looking at + // the respective size of the typed word and the suggestion if it matters sometime + // in the future. + suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq, + SuggestedWordInfo.KIND_CORRECTION, mDictType)); + if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false; } if (null != node.mShortcutTargets) { final int length = node.mShortcutTargets.size(); for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) { final char[] shortcut = node.mShortcutTargets.get(shortcutIndex); - if (!callback.addWord(shortcut, 0, shortcut.length, finalFreq, mDicTypeId, - Dictionary.UNIGRAM)) { - return false; - } + suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length), + finalFreq, SuggestedWordInfo.KIND_SHORTCUT, mDictType)); + if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false; } } return true; @@ -408,12 +429,12 @@ public class ExpandableDictionary extends Dictionary { * case we skip over some punctuations such as apostrophe in the traversal. That is, if you type * "wouldve", it could be matching "would've", so the depth will be one more than the * inputIndex - * @param callback the callback class for adding a word + * @param suggestions the list in which to add suggestions */ // TODO: Share this routine with the native code for BinaryDictionary protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word, final int depth, final boolean completion, int snr, int inputIndex, int skipPos, - WordCallback callback) { + final ArrayList<SuggestedWordInfo> suggestions) { final int count = roots.mLength; final int codeSize = mInputLength; // Optimization: Prune out words that are too long compared to how much was typed. @@ -443,14 +464,14 @@ public class ExpandableDictionary extends Dictionary { } else { finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength); } - if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, callback)) { + if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, suggestions)) { // No space left in the queue, bail out return; } } if (children != null) { getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex, - skipPos, callback); + skipPos, suggestions); } } else if ((c == Keyboard.CODE_SINGLE_QUOTE && currentChars[0] != Keyboard.CODE_SINGLE_QUOTE) || depth == skipPos) { @@ -458,7 +479,7 @@ public class ExpandableDictionary extends Dictionary { word[depth] = c; if (children != null) { getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, - skipPos, callback); + skipPos, suggestions); } } else { // Don't use alternatives if we're looking for missing characters @@ -483,7 +504,7 @@ public class ExpandableDictionary extends Dictionary { snr * addedAttenuation, mInputLength); } if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, - callback)) { + suggestions)) { // No space left in the queue, bail out return; } @@ -491,12 +512,12 @@ public class ExpandableDictionary extends Dictionary { if (children != null) { getWordsRec(children, codes, word, depth + 1, true, snr * addedAttenuation, inputIndex + 1, - skipPos, callback); + skipPos, suggestions); } } else if (children != null) { getWordsRec(children, codes, word, depth + 1, false, snr * addedAttenuation, inputIndex + 1, - skipPos, callback); + skipPos, suggestions); } } } @@ -514,8 +535,10 @@ public class ExpandableDictionary extends Dictionary { /** * Adds bigrams to the in-memory trie structure that is being used to retrieve any word + * @param word1 the first word of this bigram + * @param word2 the second word of this bigram * @param frequency frequency for this bigram - * @param addFrequency if true, it adds to current frequency, else it overwrites the old value + * @param fcp an instance of ForgettingCurveParams to use for decay policy * @return returns the final bigram frequency */ private int setBigramAndGetFrequency( @@ -580,32 +603,14 @@ public class ExpandableDictionary extends Dictionary { return searchWord(childNode.mChildren, word, depth + 1, childNode); } - // @VisibleForTesting - boolean reloadDictionaryIfRequired() { - synchronized (mUpdatingLock) { - // If we need to update, start off a background task - if (mRequiresReload) startDictionaryLoadingTaskLocked(); - // Currently updating contacts, don't return any results. - return mUpdatingDictionary; - } - } - private void runBigramReverseLookUp(final CharSequence previousWord, - final WordCallback callback) { + final ArrayList<SuggestedWordInfo> suggestions) { // Search for the lowercase version of the word only, because that's where bigrams // store their sons. Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0, previousWord.length()); if (prevWord != null && prevWord.mNGrams != null) { - reverseLookUp(prevWord.mNGrams, callback); - } - } - - @Override - public void getBigrams(final WordComposer codes, final CharSequence previousWord, - final WordCallback callback) { - if (!reloadDictionaryIfRequired()) { - runBigramReverseLookUp(previousWord, callback); + reverseLookUp(prevWord.mNGrams, suggestions); } } @@ -633,11 +638,12 @@ public class ExpandableDictionary extends Dictionary { /** * reverseLookUp retrieves the full word given a list of terminal nodes and adds those words - * through callback. + * to the suggestions list passed as an argument. * @param terminalNodes list of terminal nodes we want to add + * @param suggestions the suggestion collection to add the word to */ private void reverseLookUp(LinkedList<NextWord> terminalNodes, - final WordCallback callback) { + final ArrayList<SuggestedWordInfo> suggestions) { Node node; int freq; for (NextWord nextWord : terminalNodes) { @@ -651,8 +657,9 @@ public class ExpandableDictionary extends Dictionary { } while (node != null); if (freq >= 0) { - callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index, - freq, mDicTypeId, Dictionary.BIGRAM); + suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index, + BinaryDictionary.MAX_WORD_LENGTH - index), + freq, SuggestedWordInfo.KIND_CORRECTION, mDictType)); } } } diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 229ae2f3c..9c32f947c 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -29,7 +29,6 @@ public class InputAttributes { final public boolean mInputTypeNoAutoCorrect; final public boolean mIsSettingsSuggestionStripOn; final public boolean mApplicationSpecifiedCompletionOn; - final public int mEditorAction; public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) { final int inputType = null != editorInfo ? editorInfo.inputType : 0; @@ -92,8 +91,6 @@ public class InputAttributes { mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode; } - mEditorAction = (editorInfo == null) ? EditorInfo.IME_ACTION_UNSPECIFIED - : editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION; } @SuppressWarnings("unused") diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java new file mode 100644 index 000000000..cd53bcd13 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +public class InputPointers { + private final ScalableIntArray mXCoordinates = new ScalableIntArray(); + private final ScalableIntArray mYCoordinates = new ScalableIntArray(); + private final ScalableIntArray mPointerIds = new ScalableIntArray(); + private final ScalableIntArray mTimes = new ScalableIntArray(); + + public void addPointer(int index, int x, int y, int pointerId, int time) { + mXCoordinates.add(index, x); + mYCoordinates.add(index, y); + mPointerIds.add(index, pointerId); + mTimes.add(index, time); + } + + public void addPointer(int x, int y, int pointerId, int time) { + mXCoordinates.add(x); + mYCoordinates.add(y); + mPointerIds.add(pointerId); + mTimes.add(time); + } + + public void set(InputPointers ip) { + mXCoordinates.set(ip.mXCoordinates); + mYCoordinates.set(ip.mYCoordinates); + mPointerIds.set(ip.mPointerIds); + mTimes.set(ip.mTimes); + } + + public void copy(InputPointers ip) { + mXCoordinates.copy(ip.mXCoordinates); + mYCoordinates.copy(ip.mYCoordinates); + mPointerIds.copy(ip.mPointerIds); + mTimes.copy(ip.mTimes); + } + + /** + * Append the pointers in the specified {@link InputPointers} to the end of this. + * @param src the source {@link InputPointers} to append the pointers. + * @param startPos the starting index of the pointers in {@code src}. + * @param length the number of pointers to be appended. + */ + public void append(InputPointers src, int startPos, int length) { + final int currentLength = getPointerSize(); + final int newLength = currentLength + length; + mXCoordinates.ensureCapacity(newLength); + mYCoordinates.ensureCapacity(newLength); + mPointerIds.ensureCapacity(newLength); + mTimes.ensureCapacity(newLength); + System.arraycopy(src.getXCoordinates(), startPos, getXCoordinates(), currentLength, length); + System.arraycopy(src.getYCoordinates(), startPos, getYCoordinates(), currentLength, length); + System.arraycopy(src.getPointerIds(), startPos, getPointerIds(), currentLength, length); + System.arraycopy(src.getTimes(), startPos, getTimes(), currentLength, length); + } + + public void reset() { + mXCoordinates.reset(); + mYCoordinates.reset(); + mPointerIds.reset(); + mTimes.reset(); + } + + public int getPointerSize() { + return mXCoordinates.getLength(); + } + + public int[] getXCoordinates() { + return mXCoordinates.getPrimitiveArray(); + } + + public int[] getYCoordinates() { + return mYCoordinates.getPrimitiveArray(); + } + + public int[] getPointerIds() { + return mPointerIds.getPrimitiveArray(); + } + + public int[] getTimes() { + return mTimes.getPrimitiveArray(); + } + + private static class ScalableIntArray { + private static final int DEFAULT_SIZE = BinaryDictionary.MAX_WORD_LENGTH; + private int[] mArray; + private int mLength; + + public ScalableIntArray() { + reset(); + } + + public void add(int index, int val) { + if (mLength < index + 1) { + mLength = index; + add(val); + } else { + mArray[index] = val; + } + } + + public void add(int val) { + final int nextLength = mLength + 1; + ensureCapacity(nextLength); + mArray[mLength] = val; + mLength = nextLength; + } + + public void ensureCapacity(int minimumCapacity) { + if (mArray.length < minimumCapacity) { + final int nextCapacity = mArray.length * 2; + grow(minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity); + } + } + + private void grow(int newCapacity) { + final int[] newArray = new int[newCapacity]; + System.arraycopy(mArray, 0, newArray, 0, mArray.length); + mArray = newArray; + } + + public int getLength() { + return mLength; + } + + public void reset() { + mArray = new int[DEFAULT_SIZE]; + mLength = 0; + } + + public int[] getPrimitiveArray() { + return mArray; + } + + public void copy(ScalableIntArray ip) { + ensureCapacity(ip.mLength); + System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength); + mLength = ip.mLength; + } + + public void set(ScalableIntArray ip) { + mArray = ip.mArray; + mLength = ip.mLength; + } + } +} diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 4e1f5fe92..974af2584 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -41,26 +41,26 @@ public class LastComposedWord { public static final int NOT_A_SEPARATOR = -1; public final int[] mPrimaryKeyCodes; - public final int[] mXCoordinates; - public final int[] mYCoordinates; public final String mTypedWord; public final String mCommittedWord; public final int mSeparatorCode; public final CharSequence mPrevWord; + public final InputPointers mInputPointers = new InputPointers(); private boolean mActive; public static final LastComposedWord NOT_A_COMPOSED_WORD = - new LastComposedWord(null, null, null, "", "", NOT_A_SEPARATOR, null); + new LastComposedWord(null, null, "", "", NOT_A_SEPARATOR, null); // Warning: this is using the passed objects as is and fully expects them to be // immutable. Do not fiddle with their contents after you passed them to this constructor. - public LastComposedWord(final int[] primaryKeyCodes, final int[] xCoordinates, - final int[] yCoordinates, final String typedWord, final String committedWord, + public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers, + final String typedWord, final String committedWord, final int separatorCode, final CharSequence prevWord) { mPrimaryKeyCodes = primaryKeyCodes; - mXCoordinates = xCoordinates; - mYCoordinates = yCoordinates; + if (inputPointers != null) { + mInputPointers.copy(inputPointers); + } mTypedWord = typedWord; mCommittedWord = committedWord; mSeparatorCode = separatorCode; @@ -73,10 +73,10 @@ public class LastComposedWord { } public boolean canRevertCommit() { - return mActive && !TextUtils.isEmpty(mCommittedWord); + return mActive && !TextUtils.isEmpty(mCommittedWord) && !didCommitTypedWord(); } - public boolean didCommitTypedWord() { + private boolean didCommitTypedWord() { return TextUtils.equals(mTypedWord, mCommittedWord); } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 97e898af9..6d902b89a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -48,15 +48,12 @@ import android.util.Printer; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; -import android.view.ViewParent; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; @@ -64,6 +61,7 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.compat.CompatUtils; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.compat.SuggestionSpanUtils; +import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; @@ -103,27 +101,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ private static final String SCHEME_PACKAGE = "package"; - /** Whether to use the binary version of the contacts dictionary */ - public static final boolean USE_BINARY_CONTACTS_DICTIONARY = true; - - /** Whether to use the binary version of the user dictionary */ - public static final boolean USE_BINARY_USER_DICTIONARY = true; - - // TODO: migrate this to SettingsValues - private int mSuggestionVisibility; - private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE - = R.string.prefs_suggestion_visibility_show_value; - private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE - = R.string.prefs_suggestion_visibility_show_only_portrait_value; - private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE - = R.string.prefs_suggestion_visibility_hide_value; - - private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] { - SUGGESTION_VISIBILILTY_SHOW_VALUE, - SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE, - SUGGESTION_VISIBILILTY_HIDE_VALUE - }; - private static final int SPACE_STATE_NONE = 0; // Double space: the state where the user pressed space twice quickly, which LatinIME // resolved as period-space. Undoing this converts the period to a space. @@ -143,8 +120,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Current space state of the input method. This can be any of the above constants. private int mSpaceState; - private SettingsValues mSettingsValues; - private InputAttributes mInputAttributes; + private SettingsValues mCurrentSettings; private View mExtractArea; private View mKeyPreviewBackingView; @@ -162,15 +138,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mShouldSwitchToLastSubtype = true; private boolean mIsMainDictionaryAvailable; - // TODO: revert this back to the concrete class after transition. - private Dictionary mUserDictionary; + private UserBinaryDictionary mUserDictionary; private UserHistoryDictionary mUserHistoryDictionary; private boolean mIsUserDictionaryAvailable; private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; private WordComposer mWordComposer = new WordComposer(); - - private int mCorrectionMode; + private RichInputConnection mConnection = new RichInputConnection(this); // Keep track of the last selection range to decide if we need to show word alternatives private static final int NOT_A_CURSOR_POSITION = -1; @@ -203,14 +177,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> { private static final int MSG_UPDATE_SHIFT_STATE = 1; - private static final int MSG_SPACE_TYPED = 4; - private static final int MSG_SET_BIGRAM_PREDICTIONS = 5; private static final int MSG_PENDING_IMS_CALLBACK = 6; - private static final int MSG_UPDATE_SUGGESTIONS = 7; + private static final int MSG_UPDATE_SUGGESTION_STRIP = 7; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; private long mDoubleSpacesTurnIntoPeriodTimeout; + private long mDoubleSpaceTimerStart; public UIHandler(LatinIME outerInstance) { super(outerInstance); @@ -231,29 +204,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final LatinIME latinIme = getOuterInstance(); final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; switch (msg.what) { - case MSG_UPDATE_SUGGESTIONS: - latinIme.updateSuggestions(); + case MSG_UPDATE_SUGGESTION_STRIP: + latinIme.updateSuggestionsOrPredictions(); break; case MSG_UPDATE_SHIFT_STATE: switcher.updateShiftState(); break; - case MSG_SET_BIGRAM_PREDICTIONS: - latinIme.updateBigramPredictions(); - break; } } - public void postUpdateSuggestions() { - removeMessages(MSG_UPDATE_SUGGESTIONS); - sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions); + public void postUpdateSuggestionStrip() { + sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions); } - public void cancelUpdateSuggestions() { - removeMessages(MSG_UPDATE_SUGGESTIONS); + public void cancelUpdateSuggestionStrip() { + removeMessages(MSG_UPDATE_SUGGESTION_STRIP); } public boolean hasPendingUpdateSuggestions() { - return hasMessages(MSG_UPDATE_SUGGESTIONS); + return hasMessages(MSG_UPDATE_SUGGESTION_STRIP); } public void postUpdateShiftState() { @@ -265,26 +234,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen removeMessages(MSG_UPDATE_SHIFT_STATE); } - public void postUpdateBigramPredictions() { - removeMessages(MSG_SET_BIGRAM_PREDICTIONS); - sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions); - } - - public void cancelUpdateBigramPredictions() { - removeMessages(MSG_SET_BIGRAM_PREDICTIONS); - } - public void startDoubleSpacesTimer() { - removeMessages(MSG_SPACE_TYPED); - sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout); + mDoubleSpaceTimerStart = SystemClock.uptimeMillis(); } public void cancelDoubleSpacesTimer() { - removeMessages(MSG_SPACE_TYPED); + mDoubleSpaceTimerStart = 0; } public boolean isAcceptingDoubleSpaces() { - return hasMessages(MSG_SPACE_TYPED); + return SystemClock.uptimeMillis() - mDoubleSpaceTimerStart + < mDoubleSpacesTurnIntoPeriodTimeout; } // Working variables for the following methods. @@ -393,7 +353,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mPrefs = prefs; LatinImeLogger.init(this, prefs); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.init(this, prefs); + ResearchLogger.getInstance().init(this, prefs); } InputMethodManagerCompatWrapper.init(this); SubtypeSwitcher.init(this); @@ -411,14 +371,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen loadSettings(); - ImfUtils.setAdditionalInputMethodSubtypes(this, mSettingsValues.getAdditionalSubtypes()); - - // TODO: remove the following when it's not needed by updateCorrectionMode() any more - mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); - updateCorrectionMode(); + ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes()); Utils.GCUtils.getInstance().reset(); boolean tryGC = true; + // Shouldn't this be removed? I think that from Honeycomb on, the GC is now actually working + // as expected and this code is useless. for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { try { initSuggest(); @@ -454,14 +412,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); + final InputAttributes inputAttributes = + new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode()); final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { @Override protected SettingsValues job(Resources res) { - return new SettingsValues(mPrefs, LatinIME.this); + return new SettingsValues(mPrefs, inputAttributes, LatinIME.this); } }; - mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale()); - mFeedbackManager = new AudioAndHapticFeedbackManager(this, mSettingsValues); + mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale()); + mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings); resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); } @@ -469,7 +429,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final String localeStr = subtypeLocale.toString(); - final Dictionary oldContactsDictionary; + final ContactsBinaryDictionary oldContactsDictionary; if (mSuggest != null) { oldContactsDictionary = mSuggest.getContactsDictionary(); mSuggest.close(); @@ -477,19 +437,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen oldContactsDictionary = null; } mSuggest = new Suggest(this, subtypeLocale); - if (mSettingsValues.mAutoCorrectEnabled) { - mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); + if (mCurrentSettings.mCorrectionEnabled) { + mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); } mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale); - - if (USE_BINARY_USER_DICTIONARY) { - mUserDictionary = new UserBinaryDictionary(this, localeStr); - mIsUserDictionaryAvailable = ((UserBinaryDictionary)mUserDictionary).isEnabled(); - } else { - mUserDictionary = new UserDictionary(this, localeStr); - mIsUserDictionaryAvailable = ((UserDictionary)mUserDictionary).isEnabled(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().initSuggest(mSuggest); } + + mUserDictionary = new UserBinaryDictionary(this, localeStr); + mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); mSuggest.setUserDictionary(mUserDictionary); resetContactsDictionary(oldContactsDictionary); @@ -497,44 +455,43 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); - mUserHistoryDictionary = UserHistoryDictionary.getInstance( - this, localeStr, Suggest.DIC_USER_HISTORY, mPrefs); + mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs); mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); } /** * Resets the contacts dictionary in mSuggest according to the user settings. * - * This method takes an optional contacts dictionary to use. Since the contacts dictionary - * does not depend on the locale, it can be reused across different instances of Suggest. - * The dictionary will also be opened or closed as necessary depending on the settings. + * This method takes an optional contacts dictionary to use when the locale hasn't changed + * since the contacts dictionary can be opened or closed as necessary depending on the settings. * * @param oldContactsDictionary an optional dictionary to use, or null */ - private void resetContactsDictionary(final Dictionary oldContactsDictionary) { - final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict); + private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) { + final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict); - final Dictionary dictionaryToUse; + final ContactsBinaryDictionary dictionaryToUse; if (!shouldSetDictionary) { // Make sure the dictionary is closed. If it is already closed, this is a no-op, // so it's safe to call it anyways. if (null != oldContactsDictionary) oldContactsDictionary.close(); dictionaryToUse = null; - } else if (null != oldContactsDictionary) { - // Make sure the old contacts dictionary is opened. If it is already open, this is a - // no-op, so it's safe to call it anyways. - if (USE_BINARY_CONTACTS_DICTIONARY) { - ((ContactsBinaryDictionary)oldContactsDictionary).reopen(this); - } else { - ((ContactsDictionary)oldContactsDictionary).reopen(this); - } - dictionaryToUse = oldContactsDictionary; } else { - if (USE_BINARY_CONTACTS_DICTIONARY) { - dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS, - mSubtypeSwitcher.getCurrentSubtypeLocale()); + final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale(); + if (null != oldContactsDictionary) { + if (!oldContactsDictionary.mLocale.equals(locale)) { + // If the locale has changed then recreate the contacts dictionary. This + // allows locale dependent rules for handling bigram name predictions. + oldContactsDictionary.close(); + dictionaryToUse = new ContactsBinaryDictionary(this, locale); + } else { + // Make sure the old contacts dictionary is opened. If it is already open, + // this is a no-op, so it's safe to call it anyways. + oldContactsDictionary.reopen(this); + dictionaryToUse = oldContactsDictionary; + } } else { - dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS); + dictionaryToUse = new ContactsBinaryDictionary(this, locale); } } @@ -569,9 +526,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mDisplayOrientation != conf.orientation) { mDisplayOrientation = conf.orientation; mHandler.startOrientationChanging(); - final InputConnection ic = getCurrentInputConnection(); - commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); - if (ic != null) ic.finishComposingText(); // For voice input + mConnection.beginBatchEdit(); + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + mConnection.finishComposingText(); + mConnection.endBatchEdit(); if (isShowingOptionDialog()) mOptionsDialog.dismiss(); } @@ -660,6 +618,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0)); } if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().start(); ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs); } if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { @@ -699,7 +658,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen updateFullscreenMode(); mLastSelectionStart = editorInfo.initialSelStart; mLastSelectionEnd = editorInfo.initialSelEnd; - mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); mApplicationSpecifiedCompletions = null; inputView.closing(); @@ -709,36 +667,40 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSpaceState = SPACE_STATE_NONE; loadSettings(); - updateCorrectionMode(); - updateSuggestionVisibility(mResources); - if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) { - mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); + if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) { + mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold); } - switcher.loadKeyboard(editorInfo, mSettingsValues); + switcher.loadKeyboard(editorInfo, mCurrentSettings); if (mSuggestionsView != null) mSuggestionsView.clear(); setSuggestionStripShownInternal( isSuggestionsStripVisible(), /* needsInputViewShown */ false); - // Delay updating suggestions because keyboard input view may not be shown at this point. - mHandler.postUpdateSuggestions(); + + mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacesTimer(); - inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn, - mSettingsValues.mKeyPreviewPopupDismissDelay); + inputView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn, + mCurrentSettings.mKeyPreviewPopupDismissDelay); inputView.setProximityCorrectionEnabled(true); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } + // Callback for the TargetApplicationGetter + @Override public void onTargetApplicationKnown(final ApplicationInfo info) { mTargetApplicationInfo = info; } @Override public void onWindowHidden() { + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd, + getCurrentInputConnection()); + } super.onWindowHidden(); KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); @@ -748,6 +710,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super.onFinishInput(); LatinImeLogger.commit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().stop(); + } KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); @@ -759,7 +724,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.cancelAllMessages(); // Remove pending messages related to update suggestions - mHandler.cancelUpdateSuggestions(); + mHandler.cancelUpdateSuggestionStrip(); } @Override @@ -768,7 +733,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen int composingSpanStart, int composingSpanEnd) { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, composingSpanEnd); - if (DEBUG) { Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd @@ -780,9 +744,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", ce=" + composingSpanEnd); } if (ProductionFlag.IS_EXPERIMENTAL) { + final boolean expectingUpdateSelectionFromLogger = + ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection(); ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, - composingSpanEnd); + composingSpanEnd, mExpectingUpdateSelection, + expectingUpdateSelectionFromLogger, mConnection); + if (expectingUpdateSelectionFromLogger) { + // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work + return; + } } // TODO: refactor the following code to be less contrived. @@ -841,7 +812,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ @Override public void onExtractedTextClicked() { - if (isSuggestionsRequested()) return; + if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return; super.onExtractedTextClicked(); } @@ -857,7 +828,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ @Override public void onExtractedCursorMovement(int dx, int dy) { - if (isSuggestionsRequested()) return; + if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return; super.onExtractedCursorMovement(dx, dy); } @@ -888,33 +859,31 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); } - if (mInputAttributes.mApplicationSpecifiedCompletionOn) { - mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; - if (applicationSpecifiedCompletions == null) { - clearSuggestions(); - return; - } - - final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords = - SuggestedWords.getFromApplicationSpecifiedCompletions( - applicationSpecifiedCompletions); - final SuggestedWords suggestedWords = new SuggestedWords( - applicationSuggestedWords, - false /* typedWordValid */, - false /* hasAutoCorrectionCandidate */, - false /* allowsToBeAutoCorrected */, - false /* isPunctuationSuggestions */, - false /* isObsoleteSuggestions */, - false /* isPrediction */); - // When in fullscreen mode, show completions generated by the application - final boolean isAutoCorrection = false; - setSuggestions(suggestedWords, isAutoCorrection); - setAutoCorrectionIndicator(isAutoCorrection); - // TODO: is this the right thing to do? What should we auto-correct to in - // this case? This says to keep whatever the user typed. - mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); - setSuggestionStripShown(true); + if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; + mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; + if (applicationSpecifiedCompletions == null) { + clearSuggestions(); + return; } + + final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords = + SuggestedWords.getFromApplicationSpecifiedCompletions( + applicationSpecifiedCompletions); + final SuggestedWords suggestedWords = new SuggestedWords( + applicationSuggestedWords, + false /* typedWordValid */, + false /* hasAutoCorrectionCandidate */, + false /* isPunctuationSuggestions */, + false /* isObsoleteSuggestions */, + false /* isPrediction */); + // When in fullscreen mode, show completions generated by the application + final boolean isAutoCorrection = false; + setSuggestions(suggestedWords, isAutoCorrection); + setAutoCorrectionIndicator(isAutoCorrection); + // TODO: is this the right thing to do? What should we auto-correct to in + // this case? This says to keep whatever the user typed. + mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); + setSuggestionStripShown(true); } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1001,7 +970,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public boolean onEvaluateFullscreenMode() { // Reread resource value here, because this method is called by framework anytime as needed. final boolean isFullscreenModeAllowed = - mSettingsValues.isFullscreenModeAllowed(getResources()); + mCurrentSettings.isFullscreenModeAllowed(getResources()); return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed; } @@ -1016,15 +985,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // This will reset the whole input state to the starting state. It will clear - // the composing word, reset the last composed word, tell the inputconnection - // and the composingStateManager about it. + // the composing word, reset the last composed word, tell the inputconnection about it. private void resetEntireInputState() { resetComposingState(true /* alsoResetLastComposedWord */); - updateSuggestions(); - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.finishComposingText(); - } + clearSuggestions(); + mConnection.finishComposingText(); } private void resetComposingState(final boolean alsoResetLastComposedWord) { @@ -1033,26 +998,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; } - public void commitTyped(final InputConnection ic, final int separatorCode) { + private void commitTyped(final int separatorCode) { if (!mWordComposer.isComposingWord()) return; final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { - if (ic != null) { - ic.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } + mConnection.commitText(typedWord, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(typedWord); } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), separatorCode, prevWord); } - updateSuggestions(); + updateSuggestionsOrPredictions(); } + // Called from the KeyboardSwitcher which needs to know auto caps state to display + // the right layout. public int getCurrentAutoCapsState() { - if (!mSettingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF; + if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF; final EditorInfo ei = getCurrentInputEditorInfo(); if (ei == null) return Constants.TextUtils.CAP_MODE_OFF; @@ -1070,27 +1035,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // unless needed. if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF; - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return Constants.TextUtils.CAP_MODE_OFF; // TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls. // Note: getCursorCapsMode() returns the current capitalization mode that is any // combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none // of them. - return ic.getCursorCapsMode(inputType); + return mConnection.getCursorCapsMode(inputType); } - // "ic" may be null - private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) { - if (null == ic) return; - CharSequence lastTwo = ic.getTextBeforeCursor(2, 0); + private void swapSwapperAndSpace() { + CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { - ic.deleteSurroundingText(2, 0); + mConnection.deleteSurroundingText(2, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(2); } - ic.commitText(lastTwo.charAt(1) + " ", 1); + mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); } @@ -1098,18 +1059,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) { - if (mCorrectionMode == Suggest.CORRECTION_NONE) return false; - if (ic == null) return false; - final CharSequence lastThree = ic.getTextBeforeCursor(3, 0); + private boolean maybeDoubleSpace() { + if (!mCurrentSettings.mCorrectionEnabled) return false; + if (!mHandler.isAcceptingDoubleSpaces()) return false; + final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 && canBeFollowedByPeriod(lastThree.charAt(0)) && lastThree.charAt(1) == Keyboard.CODE_SPACE - && lastThree.charAt(2) == Keyboard.CODE_SPACE - && mHandler.isAcceptingDoubleSpaces()) { + && lastThree.charAt(2) == Keyboard.CODE_SPACE) { mHandler.cancelDoubleSpacesTimer(); - ic.deleteSurroundingText(2, 0); - ic.commitText(". ", 1); + mConnection.deleteSurroundingText(2, 0); + mConnection.commitText(". ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_doubleSpaceAutoPeriod(); } @@ -1131,29 +1091,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET; } - // "ic" may be null - private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) { - if (ic == null) return; - final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); - if (lastOne != null && lastOne.length() == 1 - && lastOne.charAt(0) == Keyboard.CODE_SPACE) { - ic.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } - } - } - + // Callback for the SuggestionsView, to call when the "add to dictionary" hint is pressed. @Override - public boolean addWordToDictionary(String word) { - if (USE_BINARY_USER_DICTIONARY) { - ((UserBinaryDictionary)mUserDictionary).addWordToUserDictionary(word, 128); - } else { - ((UserDictionary)mUserDictionary).addWordToUserDictionary(word, 128); - } - // Suggestion strip should be updated after the operation of adding word to the - // user dictionary - mHandler.postUpdateSuggestions(); + public boolean addWordToUserDictionary(String word) { + mUserDictionary.addWordToUserDictionary(word, 128); return true; } @@ -1193,17 +1134,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void performEditorAction(int actionId) { - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } + mConnection.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_performEditorAction(actionId); } } private void handleLanguageSwitchKey() { - final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList; + final boolean includesOtherImes = mCurrentSettings.mIncludesOtherImesInLanguageSwitchList; final IBinder token = getWindow().getWindow().getAttributes().token; if (mShouldSwitchToLastSubtype) { final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype(); @@ -1221,12 +1159,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - static private void sendUpDownEnterOrBackspace(final int code, final InputConnection ic) { + private void sendUpDownEnterOrBackspace(final int code) { final long eventTime = SystemClock.uptimeMillis(); - ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, + mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); - ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); } @@ -1239,24 +1177,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because - // we want to be able to compile against the Ice Cream Sandwich SDK. - if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null - && mTargetApplicationInfo.targetSdkVersion < 16) { - // Backward compatibility mode. Before Jelly bean, the keyboard would simulate - // a hardware keyboard event on pressing enter or delete. This is bad for many - // reasons (there are race conditions with commits) but some applications are - // relying on this behavior so we continue to support it for older apps. - sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER, ic); - } else { - final String text = new String(new int[] { code }, 0, 1); - ic.commitText(text, text.length()); - } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } + // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because + // we want to be able to compile against the Ice Cream Sandwich SDK. + if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null + && mTargetApplicationInfo.targetSdkVersion < 16) { + // Backward compatibility mode. Before Jelly bean, the keyboard would simulate + // a hardware keyboard event on pressing enter or delete. This is bad for many + // reasons (there are race conditions with commits) but some applications are + // relying on this behavior so we continue to support it for older apps. + sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER); + } else { + final String text = new String(new int[] { code }, 0, 1); + mConnection.commitText(text, text.length()); + } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); } } @@ -1268,11 +1203,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mDeleteCount = 0; } mLastKeyTime = when; + mConnection.beginBatchEdit(); if (ProductionFlag.IS_EXPERIMENTAL) { - if (ResearchLogger.sIsLogging) { - ResearchLogger.getInstance().logKeyEvent(primaryCode, x, y); - } + ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); } final KeyboardSwitcher switcher = mKeyboardSwitcher; @@ -1321,16 +1255,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case Keyboard.CODE_LANGUAGE_SWITCH: handleLanguageSwitchKey(); break; - default: - if (primaryCode == Keyboard.CODE_TAB - && mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT) { - performEditorAction(EditorInfo.IME_ACTION_NEXT); - break; + case Keyboard.CODE_RESEARCH: + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().presentResearchDialog(this); } + break; + default: mSpaceState = SPACE_STATE_NONE; - if (mSettingsValues.isWordSeparator(primaryCode)) { + if (mCurrentSettings.isWordSeparator(primaryCode)) { didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState); } else { + if (SPACE_STATE_PHANTOM == spaceState) { + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + } final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) { handleCharacter(primaryCode, x, y, spaceState); @@ -1349,23 +1286,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen && primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL) mLastComposedWord.deactivate(); mEnteredText = null; + mConnection.endBatchEdit(); } + // Called from PointerTracker through the KeyboardActionListener interface @Override public void onTextInput(CharSequence text) { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - ic.beginBatchEdit(); - commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); - text = specificTldProcessingOnTextInput(ic, text); + mConnection.beginBatchEdit(); + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + text = specificTldProcessingOnTextInput(text); if (SPACE_STATE_PHANTOM == mSpaceState) { sendKeyCodePoint(Keyboard.CODE_SPACE); } - ic.commitText(text, 1); + mConnection.commitText(text, 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_commitText(text); } - ic.endBatchEdit(); + mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); mSpaceState = SPACE_STATE_NONE; @@ -1373,9 +1310,32 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetComposingState(true /* alsoResetLastComposedWord */); } - // ic may not be null - private CharSequence specificTldProcessingOnTextInput(final InputConnection ic, - final CharSequence text) { + @Override + public void onStartBatchInput() { + mConnection.beginBatchEdit(); + if (mWordComposer.isComposingWord()) { + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + mExpectingUpdateSelection = true; + // TODO: Can we remove this? + mSpaceState = SPACE_STATE_PHANTOM; + } + mConnection.endBatchEdit(); + } + + @Override + public void onEndBatchInput(CharSequence text) { + mConnection.beginBatchEdit(); + if (SPACE_STATE_PHANTOM == mSpaceState) { + sendKeyCodePoint(Keyboard.CODE_SPACE); + } + mConnection.setComposingText(text, 1); + mExpectingUpdateSelection = true; + mConnection.endBatchEdit(); + mKeyboardSwitcher.updateShiftState(); + mSpaceState = SPACE_STATE_PHANTOM; + } + + private CharSequence specificTldProcessingOnTextInput(final CharSequence text) { if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD || !Character.isLetter(text.charAt(1))) { // Not a tld: do nothing. @@ -1384,7 +1344,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // We have a TLD (or something that looks like this): make sure we don't add // a space even if currently in phantom mode. mSpaceState = SPACE_STATE_NONE; - final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); + final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_PERIOD) { return text.subSequence(1, text.length()); @@ -1393,6 +1353,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } + // Called from PointerTracker through the KeyboardActionListener interface @Override public void onCancelInput() { // User released a finger outside any key @@ -1400,24 +1361,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleBackspace(final int spaceState) { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - ic.beginBatchEdit(); - handleBackspaceWhileInBatchEdit(spaceState, ic); - ic.endBatchEdit(); - } - - // "ic" may not be null. - private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) { // In many cases, we may have to put the keyboard in auto-shift state again. mHandler.postUpdateShiftState(); - if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) { + if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) { // Cancel multi-character input: remove the text we just entered. // This is triggered on backspace after a key that inputs multiple characters, // like the smiley key or the .com key. final int length = mEnteredText.length(); - ic.deleteSurroundingText(length, 0); + mConnection.deleteSurroundingText(length, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(length); } @@ -1430,19 +1382,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isComposingWord()) { final int length = mWordComposer.size(); if (length > 0) { - mWordComposer.deleteLast(); - ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); - // If we have deleted the last remaining character of a word, then we are not - // isComposingWord() any more. - if (!mWordComposer.isComposingWord()) { - // Not composing word any more, so we can show bigrams. - mHandler.postUpdateBigramPredictions(); + // Immediately after a batch input. + if (SPACE_STATE_PHANTOM == spaceState) { + mWordComposer.reset(); } else { - // Still composing a word, so we still have letters to deduce a suggestion from. - mHandler.postUpdateSuggestions(); + mWordComposer.deleteLast(); } + mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + mHandler.postUpdateSuggestionStrip(); } else { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } @@ -1450,17 +1399,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { if (mLastComposedWord.canRevertCommit()) { Utils.Stats.onAutoCorrectionCancellation(); - revertCommit(ic); + revertCommit(); return; } if (SPACE_STATE_DOUBLE == spaceState) { - if (revertDoubleSpaceWhileInBatchEdit(ic)) { + mHandler.cancelDoubleSpacesTimer(); + if (mConnection.revertDoubleSpace()) { // No need to reset mSpaceState, it has already be done (that's why we // receive it as a parameter) return; } } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) { - if (revertSwapPunctuation(ic)) { + if (mConnection.revertSwapPunctuation()) { // Likewise return; } @@ -1471,8 +1421,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mLastSelectionStart != mLastSelectionEnd) { // If there is a selection, remove it. final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; - ic.setSelection(mLastSelectionEnd, mLastSelectionEnd); - ic.deleteSurroundingText(lengthToDelete, 0); + mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); + mConnection.deleteSurroundingText(lengthToDelete, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); } @@ -1490,40 +1440,39 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // a hardware keyboard event on pressing enter or delete. This is bad for many // reasons (there are race conditions with commits) but some applications are // relying on this behavior so we continue to support it for older apps. - sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL, ic); + sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL); } else { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); } if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } if (mDeleteCount > DELETE_ACCELERATE_AT) { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } } } - if (isSuggestionsRequested()) { - restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic); + if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { + restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(); } } } - // ic may be null - private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code, + private boolean maybeStripSpace(final int code, final int spaceState, final boolean isFromSuggestionStrip) { if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { - removeTrailingSpaceWhileInBatchEdit(ic); + mConnection.removeTrailingSpace(); return false; } else if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) && isFromSuggestionStrip) { - if (mSettingsValues.isWeakSpaceSwapper(code)) { + if (mCurrentSettings.isWeakSpaceSwapper(code)) { return true; } else { - if (mSettingsValues.isWeakSpaceStripper(code)) { - removeTrailingSpaceWhileInBatchEdit(ic); + if (mCurrentSettings.isWeakSpaceStripper(code)) { + mConnection.removeTrailingSpace(); } return false; } @@ -1534,20 +1483,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void handleCharacter(final int primaryCode, final int x, final int y, final int spaceState) { - final InputConnection ic = getCurrentInputConnection(); - if (null != ic) ic.beginBatchEdit(); - // TODO: if ic is null, does it make any sense to call this? - handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState, ic); - if (null != ic) ic.endBatchEdit(); - } - - // "ic" may be null without this crashing, but the behavior will be really strange - private void handleCharacterWhileInBatchEdit(final int primaryCode, - final int x, final int y, final int spaceState, final InputConnection ic) { boolean isComposingWord = mWordComposer.isComposingWord(); if (SPACE_STATE_PHANTOM == spaceState && - !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) { + !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) { if (isComposingWord) { // Sanity check throw new RuntimeException("Should not be composing here"); @@ -1559,8 +1498,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI // thread here. if (!isComposingWord && (isAlphabet(primaryCode) - || mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) - && isSuggestionsRequested() && !isCursorTouchingWord()) { + || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) + && mCurrentSettings.isSuggestionsRequested(mDisplayOrientation) && + !mConnection.isCursorTouchingWord(mCurrentSettings)) { // Reset entirely the composing state anyway, then start composing a new word unless // the character is a single quote. The idea here is, single quote is not a // separator and it should be treated as a normal character, except in the first @@ -1571,85 +1511,73 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // it entirely and resume suggestions on the previous word, we'd like to still // have touch coordinates for it. resetComposingState(false /* alsoResetLastComposedWord */); - clearSuggestions(); } if (isComposingWord) { - mWordComposer.add( - primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector()); - if (ic != null) { - // If it's the first letter, make note of auto-caps state - if (mWordComposer.size() == 1) { - mWordComposer.setAutoCapitalized( - getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); - } - ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); - } - mHandler.postUpdateSuggestions(); + final int keyX, keyY; + if (KeyboardActionListener.Adapter.isInvalidCoordinate(x) + || KeyboardActionListener.Adapter.isInvalidCoordinate(y)) { + keyX = x; + keyY = y; + } else { + final KeyDetector keyDetector = + mKeyboardSwitcher.getKeyboardView().getKeyDetector(); + keyX = keyDetector.getTouchX(x); + keyY = keyDetector.getTouchY(y); + } + mWordComposer.add(primaryCode, keyX, keyY); + // If it's the first letter, make note of auto-caps state + if (mWordComposer.size() == 1) { + mWordComposer.setAutoCapitalized( + getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); + } + mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, + final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); sendKeyCodePoint(primaryCode); if (swapWeakSpace) { - swapSwapperAndSpaceWhileInBatchEdit(ic); + swapSwapperAndSpace(); mSpaceState = SPACE_STATE_WEAK; } - // Some characters are not word separators, yet they don't start a new - // composing span. For these, we haven't changed the suggestion strip, and - // if the "add to dictionary" hint is shown, we should do so now. Examples of - // such characters include single quote, dollar, and others; the exact list is - // the list of characters for which we enter handleCharacterWhileInBatchEdit - // that don't match the test if ((isAlphabet...)) at the top of this method. - if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) { - mHandler.postUpdateBigramPredictions(); - } + // In case the "add to dictionary" hint was still displayed. + if (null != mSuggestionsView) mSuggestionsView.dismissAddToDictionaryHint(); } + mHandler.postUpdateSuggestionStrip(); Utils.Stats.onNonSeparator((char)primaryCode, x, y); } // Returns true if we did an autocorrection, false otherwise. private boolean handleSeparator(final int primaryCode, final int x, final int y, final int spaceState) { - // Should dismiss the "Touch again to save" message when handling separator - if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) { - mHandler.cancelUpdateBigramPredictions(); - mHandler.postUpdateSuggestions(); - } - boolean didAutoCorrect = false; // Handle separator - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.beginBatchEdit(); - } if (mWordComposer.isComposingWord()) { // In certain languages where single quote is a separator, it's better // not to auto correct, but accept the typed word. For instance, // in Italian dov' should not be expanded to dove' because the elision // requires the last vowel to be removed. - final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputAttributes.mInputTypeNoAutoCorrect; - if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { - commitCurrentAutoCorrection(primaryCode, ic); + if (mCurrentSettings.mCorrectionEnabled && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { + commitCurrentAutoCorrection(primaryCode); didAutoCorrect = true; } else { - commitTyped(ic, primaryCode); + commitTyped(primaryCode); } } - final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState, + final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); if (SPACE_STATE_PHANTOM == spaceState && - mSettingsValues.isPhantomSpacePromotingSymbol(primaryCode)) { + mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) { sendKeyCodePoint(Keyboard.CODE_SPACE); } sendKeyCodePoint(primaryCode); if (Keyboard.CODE_SPACE == primaryCode) { - if (isSuggestionsRequested()) { - if (maybeDoubleSpaceWhileInBatchEdit(ic)) { + if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { + if (maybeDoubleSpace()) { mSpaceState = SPACE_STATE_DOUBLE; } else if (!isShowingPunctuationList()) { mSpaceState = SPACE_STATE_WEAK; @@ -1657,13 +1585,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mHandler.startDoubleSpacesTimer(); - if (!isCursorTouchingWord()) { - mHandler.cancelUpdateSuggestions(); - mHandler.postUpdateBigramPredictions(); + if (!mConnection.isCursorTouchingWord(mCurrentSettings)) { + mHandler.postUpdateSuggestionStrip(); } } else { if (swapWeakSpace) { - swapSwapperAndSpaceWhileInBatchEdit(ic); + swapSwapperAndSpace(); mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; } else if (SPACE_STATE_PHANTOM == spaceState) { // If we are in phantom space state, and the user presses a separator, we want to @@ -1682,9 +1609,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Utils.Stats.onSeparator((char)primaryCode, x, y); - if (ic != null) { - ic.endBatchEdit(); - } return didAutoCorrect; } @@ -1695,63 +1619,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleClose() { - commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR); + commitTyped(LastComposedWord.NOT_A_SEPARATOR); requestHideSelf(0); LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); } - public boolean isSuggestionsRequested() { - return mInputAttributes.mIsSettingsSuggestionStripOn - && (mCorrectionMode > 0 || isShowingSuggestionsStrip()); - } - - public boolean isShowingPunctuationList() { + // TODO: make this private + // Outside LatinIME, only used by the test suite. + /* package for tests */ boolean isShowingPunctuationList() { if (mSuggestionsView == null) return false; - return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions(); - } - - public boolean isShowingSuggestionsStrip() { - return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE) - || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE - && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); + return mCurrentSettings.mSuggestPuncList == mSuggestionsView.getSuggestions(); } - public boolean isSuggestionsStripVisible() { + private boolean isSuggestionsStripVisible() { if (mSuggestionsView == null) return false; if (mSuggestionsView.isShowingAddToDictionaryHint()) return true; - if (!isShowingSuggestionsStrip()) + if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation)) return false; - if (mInputAttributes.mApplicationSpecifiedCompletionOn) + if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return true; - return isSuggestionsRequested(); + return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation); } - public void switchToKeyboardView() { - if (DEBUG) { - Log.d(TAG, "Switch to keyboard view."); - } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_switchToKeyboardView(); - } - View v = mKeyboardSwitcher.getKeyboardView(); - if (v != null) { - // Confirms that the keyboard view doesn't have parent view. - ViewParent p = v.getParent(); - if (p != null && p instanceof ViewGroup) { - ((ViewGroup) p).removeView(v); - } - setInputView(v); - } - setSuggestionStripShown(isSuggestionsStripVisible()); - updateInputViewShown(); - mHandler.postUpdateSuggestions(); - } - - public void clearSuggestions() { + private void clearSuggestions() { setSuggestions(SuggestedWords.EMPTY, false); setAutoCorrectionIndicator(false); } @@ -1765,83 +1659,89 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) { // Put a blue underline to a word in TextView which will be auto-corrected. - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator && mWordComposer.isComposingWord()) { mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator; final CharSequence textWithUnderline = getTextWithUnderline(mWordComposer.getTypedWord()); - ic.setComposingText(textWithUnderline, 1); + mConnection.setComposingText(textWithUnderline, 1); } } - public void updateSuggestions() { + private void updateSuggestionsOrPredictions() { + mHandler.cancelUpdateSuggestionStrip(); + // Check if we have a suggestion engine attached. - if ((mSuggest == null || !isSuggestionsRequested())) { + if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { if (mWordComposer.isComposingWord()) { - Log.w(TAG, "Called updateSuggestions but suggestions were not requested!"); + Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not " + + "requested!"); mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); } return; } - mHandler.cancelUpdateSuggestions(); - mHandler.cancelUpdateBigramPredictions(); - - if (!mWordComposer.isComposingWord()) { + final String typedWord = mWordComposer.getTypedWord(); + if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) { setPunctuationSuggestions(); return; } - // TODO: May need a better way of retrieving previous word - final InputConnection ic = getCurrentInputConnection(); - final CharSequence prevWord; - if (null == ic) { - prevWord = null; + // Get the word on which we should search the bigrams. If we are composing a word, it's + // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we + // should just skip whitespace if any, so 1. + // TODO: this is slow (2-way IPC) - we should probably cache this instead. + final CharSequence prevWord = + mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, + mWordComposer.isComposingWord() ? 2 : 1); + SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, + prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), + mCurrentSettings.mCorrectionEnabled); + suggestedWords = maybeRetrieveOlderSuggestions(typedWord, suggestedWords); + + if (null != suggestedWords && suggestedWords.size() > 0) { + showSuggestions(suggestedWords, typedWord); } else { - prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators); + clearSuggestions(); } + } - final CharSequence typedWord = mWordComposer.getTypedWord(); - // getSuggestedWords handles gracefully a null value of prevWord - final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, - prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode); - - // Basically, we update the suggestion strip only when suggestion count > 1. However, - // there is an exception: We update the suggestion strip whenever typed word's length - // is 1 or typed word is found in dictionary, regardless of suggestion count. Actually, - // in most cases, suggestion count is 1 when typed word's length is 1, but we do always - // need to clear the previous state when the user starts typing a word (i.e. typed word's - // length == 1). - if (suggestedWords.size() > 1 || typedWord.length() == 1 - || !suggestedWords.mAllowsToBeAutoCorrected + private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord, + final SuggestedWords suggestedWords) { + // TODO: consolidate this into getSuggestedWords + // We update the suggestion strip only when we have some suggestions to show, i.e. when + // the suggestion count is > 1; else, we leave the old suggestions, with the typed word + // replaced with the new one. However, when the word is a dictionary word, or when the + // length of the typed word is 1 or 0 (after a deletion typically), we do want to remove the + // old suggestions. Also, if we are showing the "add to dictionary" hint, we need to + // revert to suggestions - although it is unclear how we can come here if it's displayed. + if (suggestedWords.size() > 1 || typedWord.length() <= 1 + || !suggestedWords.mTypedWordValid || mSuggestionsView.isShowingAddToDictionaryHint()) { - showSuggestions(suggestedWords, typedWord); + return suggestedWords; } else { SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions(); - if (previousSuggestions == mSettingsValues.mSuggestPuncList) { + if (previousSuggestions == mCurrentSettings.mSuggestPuncList) { previousSuggestions = SuggestedWords.EMPTY; } final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions = SuggestedWords.getTypedWordAndPreviousSuggestions( typedWord, previousSuggestions); - final SuggestedWords obsoleteSuggestedWords = - new SuggestedWords(typedWordAndPreviousSuggestions, + return new SuggestedWords(typedWordAndPreviousSuggestions, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, - false /* allowsToBeAutoCorrected */, false /* isPunctuationSuggestions */, true /* isObsoleteSuggestions */, false /* isPrediction */); - showSuggestions(obsoleteSuggestedWords, typedWord); } } - public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) { + private void showSuggestions(final SuggestedWords suggestedWords, + final CharSequence typedWord) { + // This method is only ever called by updateSuggestions or updateBigramPredictions. final CharSequence autoCorrection; if (suggestedWords.size() > 0) { - if (suggestedWords.hasAutoCorrectionWord()) { + if (suggestedWords.mWillAutoCorrect) { autoCorrection = suggestedWords.getWord(1); } else { autoCorrection = typedWord; @@ -1856,12 +1756,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setSuggestionStripShown(isSuggestionsStripVisible()); } - private void commitCurrentAutoCorrection(final int separatorCodePoint, - final InputConnection ic) { + private void commitCurrentAutoCorrection(final int separatorCodePoint) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { - mHandler.cancelUpdateSuggestions(); - updateSuggestions(); + updateSuggestionsOrPredictions(); } final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull(); if (autoCorrection != null) { @@ -1878,26 +1776,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); - if (!typedWord.equals(autoCorrection) && null != ic) { + if (!typedWord.equals(autoCorrection)) { // This will make the correction flash for a short while as a visual clue // to the user that auto-correction happened. - ic.commitCorrection(new CorrectionInfo(mLastSelectionEnd - typedWord.length(), + mConnection.commitCorrection( + new CorrectionInfo(mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection)); } } } + // Called from SuggestionsView through the SuggestionsView.Listener interface @Override public void pickSuggestionManually(final int index, final CharSequence suggestion, - int x, int y) { - final InputConnection ic = getCurrentInputConnection(); - if (null != ic) ic.beginBatchEdit(); - pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y, ic); - if (null != ic) ic.endBatchEdit(); - } - - public void pickSuggestionManuallyWhileInBatchEdit(final int index, - final CharSequence suggestion, final int x, final int y, final InputConnection ic) { + final int x, final int y) { final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput if (suggestion.length() == 1 && isShowingPunctuationList()) { @@ -1915,15 +1807,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } + mConnection.beginBatchEdit(); if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0) { int firstChar = Character.codePointAt(suggestion, 0); - if ((!mSettingsValues.isWeakSpaceStripper(firstChar)) - && (!mSettingsValues.isWeakSpaceSwapper(firstChar))) { + if ((!mCurrentSettings.isWeakSpaceStripper(firstChar)) + && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) { sendKeyCodePoint(Keyboard.CODE_SPACE); } } - if (mInputAttributes.mApplicationSpecifiedCompletionOn + if (mCurrentSettings.isApplicationSpecifiedCompletionsOn() && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { if (mSuggestionsView != null) { @@ -1931,13 +1824,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mKeyboardSwitcher.updateShiftState(); resetComposingState(true /* alsoResetLastComposedWord */); - if (ic != null) { - final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; - ic.commitCompletion(completionInfo); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, - completionInfo.getText(), x, y); - } + final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; + mConnection.commitCompletion(completionInfo); + mConnection.endBatchEdit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, + completionInfo.getText(), x, y); } return; } @@ -1953,6 +1845,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); mSpaceState = SPACE_STATE_PHANTOM; @@ -1966,8 +1859,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // - There is a dictionary and the word is not in it // Please note that if mSuggest is null, it means that everything is off: suggestion // and correction, so we shouldn't try to show the hint - // We used to look at mCorrectionMode here, but showing the hint should have nothing - // to do with the autocorrection setting. final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null // If there is no dictionary the hint should be shown. && (!mSuggest.hasMainDictionary() @@ -1977,20 +1868,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); - if (!showingAddToDictionaryHint) { - // If we're not showing the "Touch again to save", then show corrections again. - // In case the cursor position doesn't change, make sure we show the suggestions again. - updateBigramPredictions(); - // Updating the predictions right away may be slow and feel unresponsive on slower - // terminals. On the other hand if we just postUpdateBigramPredictions() it will - // take a noticeable delay to update them which may feel uneasy. + if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { + mSuggestionsView.showAddToDictionaryHint(suggestion, mCurrentSettings.mHintToSaveText); } else { - if (mIsUserDictionaryAvailable) { - mSuggestionsView.showAddToDictionaryHint( - suggestion, mSettingsValues.mHintToSaveText); - } else { - mHandler.postUpdateSuggestions(); - } + // If we're not showing the "Touch again to save", then update the suggestion strip. + mHandler.postUpdateSuggestionStrip(); } } @@ -1999,22 +1881,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ private void commitChosenWord(final CharSequence chosenWord, final int commitType, final int separatorCode) { - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - if (mSettingsValues.mEnableSuggestionSpanInsertion) { - final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); - ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( - this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), - 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } - } else { - ic.commitText(chosenWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } - } + final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); + mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( + this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(chosenWord); } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); @@ -2026,42 +1897,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen separatorCode, prevWord); } - public void updateBigramPredictions() { - if (mSuggest == null || !isSuggestionsRequested()) - return; - - if (!mSettingsValues.mBigramPredictionEnabled) { - setPunctuationSuggestions(); - return; - } - - final SuggestedWords suggestedWords; - if (mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) { - final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(), - mSettingsValues.mWordSeparators); - if (!TextUtils.isEmpty(prevWord)) { - suggestedWords = mSuggest.getBigramPredictions(prevWord); - } else { - suggestedWords = null; - } - } else { - suggestedWords = null; - } - - if (null != suggestedWords && suggestedWords.size() > 0) { - // Explicitly supply an empty typed word (the no-second-arg version of - // showSuggestions will retrieve the word near the cursor, we don't want that here) - showSuggestions(suggestedWords, ""); - } else { - clearSuggestions(); - } - } - - public void setPunctuationSuggestions() { - if (mSettingsValues.mBigramPredictionEnabled) { + private void setPunctuationSuggestions() { + if (mCurrentSettings.mBigramPredictionEnabled) { clearSuggestions(); } else { - setSuggestions(mSettingsValues.mSuggestPuncList, false); + setSuggestions(mCurrentSettings.mSuggestPuncList, false); } setAutoCorrectionIndicator(false); setSuggestionStripShown(isSuggestionsStripVisible()); @@ -2070,22 +1910,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) { if (TextUtils.isEmpty(suggestion)) return null; - // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be - // adding words in situations where the user or application really didn't - // want corrections enabled or learned. - if (!(mCorrectionMode == Suggest.CORRECTION_FULL - || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) { - return null; - } + // If correction is not enabled, we don't add words to the user history dictionary. + // That's to avoid unintended additions in some sensitive fields, or fields that + // expect to receive non-words. + if (!mCurrentSettings.mCorrectionEnabled) return null; - if (mUserHistoryDictionary != null) { - final InputConnection ic = getCurrentInputConnection(); - final CharSequence prevWord; - if (null != ic) { - prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators); - } else { - prevWord = null; - } + final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary; + if (userHistoryDictionary != null) { + final CharSequence prevWord + = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2); final String secondWord; if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toString().toLowerCase( @@ -2098,95 +1931,36 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int maxFreq = AutoCorrection.getMaxFrequency( mSuggest.getUnigramDictionaries(), suggestion); if (maxFreq == 0) return null; - mUserHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(), + userHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(), secondWord, maxFreq > 0); return prevWord; } return null; } - public boolean isCursorTouchingWord() { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return false; - CharSequence before = ic.getTextBeforeCursor(1, 0); - CharSequence after = ic.getTextAfterCursor(1, 0); - if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0)) - && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) { - return true; - } - if (!TextUtils.isEmpty(after) && !mSettingsValues.isWordSeparator(after.charAt(0)) - && !mSettingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) { - return true; - } - return false; - } - - // "ic" must not be null - private static boolean sameAsTextBeforeCursor(final InputConnection ic, - final CharSequence text) { - final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0); - return TextUtils.equals(text, beforeText); - } - - // "ic" must not be null /** * Check if the cursor is actually at the end of a word. If so, restart suggestions on this * word, else do nothing. */ - private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord( - final InputConnection ic) { - // Bail out if the cursor is not at the end of a word (cursor must be preceded by - // non-whitespace, non-separator, non-start-of-text) - // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here. - final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0); - if (TextUtils.isEmpty(textBeforeCursor) - || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return; - - // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace, - // separator or end of line/text) - // Example: "test|"<EOL> "te|st" get rejected here - final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0); - if (!TextUtils.isEmpty(textAfterCursor) - && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return; - - // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe) - // Example: " -|" gets rejected here but "e-|" and "e|" are okay - CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators); - // We don't suggest on leading single quotes, so we have to remove them from the word if - // it starts with single quotes. - while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) { - word = word.subSequence(1, word.length()); - } - if (TextUtils.isEmpty(word)) return; - final char firstChar = word.charAt(0); // we just tested that word is not empty - if (word.length() == 1 && !Character.isLetter(firstChar)) return; - - // We only suggest on words that start with a letter or a symbol that is excluded from - // word separators (see #handleCharacterWhileInBatchEdit). - if (!(isAlphabet(firstChar) - || mSettingsValues.isSymbolExcludedFromWordSeparators(firstChar))) { - return; + private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() { + final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings); + if (null != word) { + restartSuggestionsOnWordBeforeCursor(word); } - - // Okay, we are at the end of a word. Restart suggestions. - restartSuggestionsOnWordBeforeCursor(ic, word); } - // "ic" must not be null - private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic, - final CharSequence word) { + private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) { mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); - ic.deleteSurroundingText(length, 0); + mConnection.deleteSurroundingText(length, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(length); } - ic.setComposingText(word, 1); - mHandler.postUpdateSuggestions(); + mConnection.setComposingText(word, 1); + mHandler.postUpdateSuggestionStrip(); } - // "ic" must not be null - private void revertCommit(final InputConnection ic) { + private void revertCommit() { final CharSequence previousWord = mLastComposedWord.mPrevWord; final String originallyTypedWord = mLastComposedWord.mTypedWord; final CharSequence committedWord = mLastComposedWord.mCommittedWord; @@ -2200,7 +1974,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen throw new RuntimeException("revertCommit, but we are composing a word"); } final String wordBeforeCursor = - ic.getTextBeforeCursor(deleteLength, 0) + mConnection.getTextBeforeCursor(deleteLength, 0) .subSequence(0, cancelLength).toString(); if (!TextUtils.equals(committedWord, wordBeforeCursor)) { throw new RuntimeException("revertCommit check failed: we thought we were " @@ -2208,7 +1982,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "\", but before the cursor we found \"" + wordBeforeCursor + "\""); } } - ic.deleteSurroundingText(deleteLength, 0); + mConnection.deleteSurroundingText(deleteLength, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(deleteLength); } @@ -2216,122 +1990,58 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); } - if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) { - // This is the case when we cancel a manual pick. - // We should restart suggestion on the word right away. - mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord); - ic.setComposingText(originallyTypedWord, 1); - } else { - ic.commitText(originallyTypedWord, 1); - // Re-insert the separator - sendKeyCodePoint(mLastComposedWord.mSeparatorCode); - Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertCommit(originallyTypedWord); - } - // Don't restart suggestion yet. We'll restart if the user deletes the - // separator. - } - mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; - mHandler.cancelUpdateBigramPredictions(); - mHandler.postUpdateSuggestions(); - } - - // "ic" must not be null - private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) { - mHandler.cancelDoubleSpacesTimer(); - // Here we test whether we indeed have a period and a space before us. This should not - // be needed, but it's there just in case something went wrong. - final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0); - if (!". ".equals(textBeforeCursor)) { - // Theoretically we should not be coming here if there isn't ". " before the - // cursor, but the application may be changing the text while we are typing, so - // anything goes. We should not crash. - Log.d(TAG, "Tried to revert double-space combo but we didn't find " - + "\". \" just before the cursor."); - return false; - } - ic.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } - ic.commitText(" ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); - } - return true; - } - - private static boolean revertSwapPunctuation(final InputConnection ic) { - // Here we test whether we indeed have a space and something else before us. This should not - // be needed, but it's there just in case something went wrong. - final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0); - // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to - // enter surrogate pairs this code will have been removed. - if (TextUtils.isEmpty(textBeforeCursor) - || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) { - // We may only come here if the application is changing the text while we are typing. - // This is quite a broken case, but not logically impossible, so we shouldn't crash, - // but some debugging log may be in order. - Log.d(TAG, "Tried to revert a swap of punctuation but we didn't " - + "find a space just before the cursor."); - return false; - } - ic.beginBatchEdit(); - ic.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } - ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1); + mConnection.commitText(originallyTypedWord, 1); + // Re-insert the separator + sendKeyCodePoint(mLastComposedWord.mSeparatorCode); + Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, + WordComposer.NOT_A_COORDINATE); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertSwapPunctuation(); + ResearchLogger.latinIME_revertCommit(originallyTypedWord); } - ic.endBatchEdit(); - return true; + // Don't restart suggestion yet. We'll restart if the user deletes the + // separator. + mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; + // We have a separator between the word and the cursor: we should show predictions. + mHandler.postUpdateSuggestionStrip(); } + // Used by the RingCharBuffer public boolean isWordSeparator(int code) { - return mSettingsValues.isWordSeparator(code); - } - - public boolean preferCapitalization() { - return mWordComposer.isFirstCharCapitalized(); + return mCurrentSettings.isWordSeparator(code); } // Notify that language or mode have been changed and toggleLanguage will update KeyboardID - // according to new language or mode. + // according to new language or mode. Called from SubtypeSwitcher. public void onRefreshKeyboard() { // When the device locale is changed in SetupWizard etc., this method may get called via // onConfigurationChanged before SoftInputWindow is shown. if (mKeyboardSwitcher.getKeyboardView() != null) { // Reload keyboard because the current language has been changed. - mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues); + mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings); } initSuggest(); - updateCorrectionMode(); loadSettings(); // Since we just changed languages, we should re-evaluate suggestions with whatever word // we are currently composing. If we are not composing anything, we may want to display - // predictions or punctuation signs (which is done by updateBigramPredictions anyway). - if (isCursorTouchingWord()) { - mHandler.postUpdateSuggestions(); - } else { - mHandler.postUpdateBigramPredictions(); - } + // predictions or punctuation signs (which is done by the updateSuggestionStrip anyway). + mHandler.postUpdateSuggestionStrip(); } // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to - // {@link KeyboardSwitcher}. + // {@link KeyboardSwitcher}. Called from KeyboardSwitcher public void hapticAndAudioFeedback(final int primaryCode) { mFeedbackManager.hapticAndAudioFeedback(primaryCode, mKeyboardSwitcher.getKeyboardView()); } + // Callback called by PointerTracker through the KeyboardActionListener. This is called when a + // key is depressed; release matching call is onReleaseKey below. @Override public void onPressKey(int primaryCode) { mKeyboardSwitcher.onPressKey(primaryCode); } + // Callback by PointerTracker through the KeyboardActionListener. This is called when a key + // is released; press matching call is onPressKey above. @Override public void onReleaseKey(int primaryCode, boolean withSliding) { mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding); @@ -2352,12 +2062,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This is a stopgap solution to avoid leaving a high surrogate alone in a text view. // In the future, we need to deprecate deteleSurroundingText() and have a surrogate // pair-friendly way of deleting characters in InputConnection. - final InputConnection ic = getCurrentInputConnection(); - if (null != ic) { - final CharSequence lastChar = ic.getTextBeforeCursor(1, 0); - if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) { - ic.deleteSurroundingText(1, 0); - } + final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0); + if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) { + mConnection.deleteSurroundingText(1, 0); } } } @@ -2375,29 +2082,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } }; - private void updateCorrectionMode() { - // TODO: cleanup messy flags - final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputAttributes.mInputTypeNoAutoCorrect; - mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE; - mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect) - ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode; - } - - private void updateSuggestionVisibility(final Resources res) { - final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting; - for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) { - if (suggestionVisiblityStr.equals(res.getString(visibility))) { - mSuggestionVisibility = visibility; - break; - } - } - } - private void launchSettings() { launchSettingsClass(SettingsActivity.class); } + // Called from debug code only public void launchDebugSettings() { launchSettingsClass(DebugSettingsActivity.class); } @@ -2440,10 +2129,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final AlertDialog.Builder builder = new AlertDialog.Builder(this) .setItems(items, listener) .setTitle(title); - showOptionDialogInternal(builder.create()); + showOptionDialog(builder.create()); } - private void showOptionDialogInternal(AlertDialog dialog) { + /* package */ void showOptionDialog(AlertDialog dialog) { final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken(); if (windowToken == null) return; @@ -2470,13 +2159,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); - p.println(" mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn); - p.println(" mCorrectionMode=" + mCorrectionMode); + p.println(" mIsSuggestionsSuggestionsRequested = " + + mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)); + p.println(" mCorrectionEnabled=" + mCurrentSettings.mCorrectionEnabled); p.println(" isComposingWord=" + mWordComposer.isComposingWord()); - p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled); - p.println(" mSoundOn=" + mSettingsValues.mSoundOn); - p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn); - p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn); - p.println(" mInputAttributes=" + mInputAttributes.toString()); + p.println(" mSoundOn=" + mCurrentSettings.mSoundOn); + p.println(" mVibrateOn=" + mCurrentSettings.mVibrateOn); + p.println(" mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn); + p.println(" inputAttributes=" + mCurrentSettings.getInputAttributesDebugString()); } } diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index dc0868e7c..e843848bc 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -71,7 +71,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static void onStartSuggestion(CharSequence previousWords) { } - public static void onAddSuggestedWord(String word, int typeId, int dataType) { + public static void onAddSuggestedWord(String word, String sourceDictionaryId) { } public static void onSetKeyboard(Keyboard kb) { diff --git a/java/src/com/android/inputmethod/latin/NativeUtils.java b/java/src/com/android/inputmethod/latin/NativeUtils.java new file mode 100644 index 000000000..9cc2bc02e --- /dev/null +++ b/java/src/com/android/inputmethod/latin/NativeUtils.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +public class NativeUtils { + static { + JniUtils.loadNativeLibrary(); + } + + private NativeUtils() { + // This utility class is not publicly instantiable. + } + + /** + * This method just calls up libm's powf() directly. + */ + public static native float powf(float x, float y); +} diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java index 66d6d58b1..2de0194fd 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLogger.java +++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java @@ -16,37 +16,47 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; + +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.os.SystemClock; -import android.preference.PreferenceManager; import android.text.TextUtils; +import android.util.JsonWriter; import android.util.Log; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.Toast; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.internal.KeyboardState; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.latin.RichInputConnection.Range; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.UUID; /** * Logs the use of the LatinIME keyboard. @@ -58,700 +68,1016 @@ import java.util.Map; */ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = ResearchLogger.class.getSimpleName(); - private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; private static final boolean DEBUG = false; + private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info + /* package */ static boolean sIsLogging = false; + private static final int OUTPUT_FORMAT_VERSION = 1; + private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; + private static final String FILENAME_PREFIX = "researchLog"; + private static final String FILENAME_SUFFIX = ".txt"; + private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( + new OutputStreamWriter(new NullOutputStream())); + private static final SimpleDateFormat TIMESTAMP_DATEFORMAT = + new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); + + // constants related to specific log points + private static final String WHITESPACE_SEPARATORS = " \t\n\r"; + private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 + private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; + + private static final ResearchLogger sInstance = new ResearchLogger(); + private HandlerThread mHandlerThread; + /* package */ Handler mLoggingHandler; + // to write to a different filename, e.g., for testing, set mFile before calling start() + private File mFilesDir; + /* package */ File mFile; + private JsonWriter mJsonWriter = NULL_JSON_WRITER; // should never be null + + private int mLoggingState; + private static final int LOGGING_STATE_OFF = 0; + private static final int LOGGING_STATE_ON = 1; + private static final int LOGGING_STATE_STOPPING = 2; + private boolean mIsPasswordView = false; + + // digits entered by the user are replaced with this codepoint. + /* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT = + Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area" + // U+E001 is in the "private-use area" + /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001"; + // set when LatinIME should ignore an onUpdateSelection() callback that + // arises from operations in this class + private static boolean sLatinIMEExpectingUpdateSelection = false; + + // used to check whether words are not unique + private Suggest mSuggest; + private Dictionary mDictionary; + + private static class NullOutputStream extends OutputStream { + /** {@inheritDoc} */ + @Override + public void write(byte[] buffer, int offset, int count) { + // nop + } - private static final ResearchLogger sInstance = new ResearchLogger(new LogFileManager()); - public static boolean sIsLogging = false; - /* package */ final Handler mLoggingHandler; - private InputMethodService mIms; - - /** - * Isolates management of files. This variable should never be null, but can be changed - * to support testing. - */ - /* package */ LogFileManager mLogFileManager; - - /** - * Manages the file(s) that stores the logs. - * - * Handles creation, deletion, and provides Readers, Writers, and InputStreams to access - * the logs. - */ - /* package */ static class LogFileManager { - public static final String RESEARCH_LOG_FILENAME_KEY = "RESEARCH_LOG_FILENAME"; + /** {@inheritDoc} */ + @Override + public void write(byte[] buffer) { + // nop + } - private static final String DEFAULT_FILENAME = "researchLog.txt"; - private static final long LOGFILE_PURGE_INTERVAL = 1000 * 60 * 60 * 24; + @Override + public void write(int oneByte) { + } + } - protected InputMethodService mIms; - protected File mFile; - protected PrintWriter mPrintWriter; + private ResearchLogger() { + mLoggingState = LOGGING_STATE_OFF; + } - /* package */ LogFileManager() { - } + public static ResearchLogger getInstance() { + return sInstance; + } - public void init(final InputMethodService ims) { - mIms = ims; + public void init(final InputMethodService ims, final SharedPreferences prefs) { + assert ims != null; + if (ims == null) { + Log.w(TAG, "IMS is null; logging is off"); + } else { + mFilesDir = ims.getFilesDir(); + if (mFilesDir == null || !mFilesDir.exists()) { + Log.w(TAG, "IME storage directory does not exist."); + } } - - public synchronized void createLogFile() throws IOException { - createLogFile(DEFAULT_FILENAME); + if (prefs != null) { + sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); + prefs.registerOnSharedPreferenceChangeListener(this); } + } - public synchronized void createLogFile(final SharedPreferences prefs) - throws IOException { - final String filename = - prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME); - createLogFile(filename); + public synchronized void start() { + Log.d(TAG, "start called"); + if (!sIsLogging) { + // Log.w(TAG, "not in usability mode; not logging"); + return; } - - public synchronized void createLogFile(final String filename) - throws IOException { - if (mIms == null) { - final String msg = "InputMethodService is not configured. Logging is off."; - Log.w(TAG, msg); - throw new IOException(msg); + if (mFilesDir == null || !mFilesDir.exists()) { + Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); + } else { + if (mHandlerThread == null || !mHandlerThread.isAlive()) { + mHandlerThread = new HandlerThread("ResearchLogger logging task", + Process.THREAD_PRIORITY_BACKGROUND); + mHandlerThread.start(); + mLoggingHandler = null; + mLoggingState = LOGGING_STATE_OFF; } - final File filesDir = mIms.getFilesDir(); - if (filesDir == null || !filesDir.exists()) { - final String msg = "Storage directory does not exist. Logging is off."; - Log.w(TAG, msg); - throw new IOException(msg); + if (mLoggingHandler == null) { + mLoggingHandler = new Handler(mHandlerThread.getLooper()); + mLoggingState = LOGGING_STATE_OFF; } - close(); - final File file = new File(filesDir, filename); - mFile = file; - boolean append = true; - if (file.exists() && file.lastModified() + LOGFILE_PURGE_INTERVAL < - System.currentTimeMillis()) { - append = false; + if (mFile == null) { + final String timestampString = TIMESTAMP_DATEFORMAT.format(new Date()); + mFile = new File(mFilesDir, FILENAME_PREFIX + timestampString + FILENAME_SUFFIX); } - mPrintWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, append)), true); - } - - public synchronized boolean append(final String s) { - PrintWriter printWriter = mPrintWriter; - if (printWriter == null || !mFile.exists()) { - if (DEBUG) { - Log.w(TAG, "PrintWriter is null... attempting to create default log file"); - } + if (mLoggingState == LOGGING_STATE_OFF) { try { - createLogFile(); - printWriter = mPrintWriter; + mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); + mJsonWriter.setLenient(true); + mJsonWriter.beginArray(); + mLoggingState = LOGGING_STATE_ON; } catch (IOException e) { - Log.w(TAG, "Failed to create log file. Not logging."); - return false; + Log.w(TAG, "cannot start JsonWriter"); + mJsonWriter = NULL_JSON_WRITER; + e.printStackTrace(); } } - printWriter.print(s); - printWriter.flush(); - return !printWriter.checkError(); } + } - public synchronized void reset() { - if (mPrintWriter != null) { - mPrintWriter.close(); - mPrintWriter = null; - if (DEBUG) { - Log.d(TAG, "logfile closed"); - } - } - if (mFile != null) { - mFile.delete(); - if (DEBUG) { - Log.d(TAG, "logfile deleted"); + public synchronized void stop() { + Log.d(TAG, "stop called"); + if (mLoggingHandler != null && mLoggingState == LOGGING_STATE_ON) { + mLoggingState = LOGGING_STATE_STOPPING; + flushEventQueue(true); + // put this in the Handler queue so pending writes are processed first. + mLoggingHandler.post(new Runnable() { + @Override + public void run() { + try { + Log.d(TAG, "closing jsonwriter"); + mJsonWriter.endArray(); + mJsonWriter.flush(); + mJsonWriter.close(); + } catch (IllegalStateException e1) { + // assume that this is just the json not being terminated properly. + // ignore + e1.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + mJsonWriter = NULL_JSON_WRITER; + mFile = null; + mLoggingState = LOGGING_STATE_OFF; + if (DEBUG) { + Log.d(TAG, "logfile closed"); + } + Log.d(TAG, "finished stop(), notifying"); + synchronized (ResearchLogger.this) { + ResearchLogger.this.notify(); + } + } } - mFile = null; + }); + try { + wait(); + } catch (InterruptedException e) { + e.printStackTrace(); } } + } - public synchronized void close() { - if (mPrintWriter != null) { - mPrintWriter.close(); - mPrintWriter = null; + public synchronized boolean abort() { + Log.d(TAG, "abort called"); + boolean isLogFileDeleted = false; + if (mLoggingHandler != null && mLoggingState == LOGGING_STATE_ON) { + mLoggingState = LOGGING_STATE_STOPPING; + try { + Log.d(TAG, "closing jsonwriter"); + mJsonWriter.endArray(); + mJsonWriter.close(); + } catch (IllegalStateException e1) { + // assume that this is just the json not being terminated properly. + // ignore + e1.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + mJsonWriter = NULL_JSON_WRITER; + // delete file + final boolean isDeleted = mFile.delete(); + if (isDeleted) { + isLogFileDeleted = true; + } mFile = null; + mLoggingState = LOGGING_STATE_OFF; if (DEBUG) { Log.d(TAG, "logfile closed"); } } } + return isLogFileDeleted; + } - /* package */ synchronized void flush() { - if (mPrintWriter != null) { - mPrintWriter.flush(); - } + /* package */ synchronized void flush() { + try { + mJsonWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); } + } - /* package */ synchronized String getContents() { - final File file = mFile; - if (file == null) { - return ""; - } - if (mPrintWriter != null) { - mPrintWriter.flush(); - } - FileInputStream stream = null; - FileChannel fileChannel = null; - String s = ""; - try { - stream = new FileInputStream(file); - fileChannel = stream.getChannel(); - final ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length()); - fileChannel.read(byteBuffer); - byteBuffer.rewind(); - CharBuffer charBuffer = Charset.defaultCharset().decode(byteBuffer); - s = charBuffer.toString(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (fileChannel != null) { - fileChannel.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (stream != null) { - stream.close(); + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + if (key == null || prefs == null) { + return; + } + sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); + if (sIsLogging == false) { + abort(); + } + } + + /* package */ void presentResearchDialog(final LatinIME latinIME) { + final CharSequence title = latinIME.getString(R.string.english_ime_research_log); + final CharSequence[] items = new CharSequence[] { + latinIME.getString(R.string.note_timestamp_for_researchlog), + latinIME.getString(R.string.do_not_log_this_session), + }; + final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface di, int position) { + di.dismiss(); + switch (position) { + case 0: + ResearchLogger.getInstance().userTimestamp(); + Toast.makeText(latinIME, R.string.notify_recorded_timestamp, + Toast.LENGTH_LONG).show(); + break; + case 1: + Toast toast = Toast.makeText(latinIME, + R.string.notify_session_log_deleting, Toast.LENGTH_LONG); + toast.show(); + final ResearchLogger logger = ResearchLogger.getInstance(); + boolean isLogDeleted = logger.abort(); + toast.cancel(); + if (isLogDeleted) { + Toast.makeText(latinIME, R.string.notify_session_log_deleted, + Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(latinIME, + R.string.notify_session_log_not_deleted, Toast.LENGTH_LONG) + .show(); } - } catch (IOException e) { - e.printStackTrace(); - } + break; } } - return s; - } + }; + final AlertDialog.Builder builder = new AlertDialog.Builder(latinIME) + .setItems(items, listener) + .setTitle(title); + latinIME.showOptionDialog(builder.create()); } - private ResearchLogger(final LogFileManager logFileManager) { - final HandlerThread handlerThread = new HandlerThread("ResearchLogger logging task", - Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - mLoggingHandler = new Handler(handlerThread.getLooper()); - mLogFileManager = logFileManager; + public void initSuggest(Suggest suggest) { + mSuggest = suggest; } - public static ResearchLogger getInstance() { - return sInstance; + private void setIsPasswordView(boolean isPasswordView) { + mIsPasswordView = isPasswordView; } - public static void init(final InputMethodService ims, final SharedPreferences prefs) { - sInstance.initInternal(ims, prefs); + private boolean isAllowedToLog() { + return mLoggingState == LOGGING_STATE_ON && !mIsPasswordView; } - /* package */ void initInternal(final InputMethodService ims, final SharedPreferences prefs) { - mIms = ims; - final LogFileManager logFileManager = mLogFileManager; - if (logFileManager != null) { - logFileManager.init(ims); - try { - logFileManager.createLogFile(prefs); - } catch (IOException e) { - e.printStackTrace(); + private static final String CURRENT_TIME_KEY = "_ct"; + private static final String UPTIME_KEY = "_ut"; + private static final String EVENT_TYPE_KEY = "_ty"; + private static final Object[] EVENTKEYS_NULLVALUES = {}; + + private LogUnit mCurrentLogUnit = new LogUnit(); + + /** + * Buffer a research log event, flagging it as privacy-sensitive. + * + * This event contains potentially private information. If the word that this event is a part + * of is determined to be privacy-sensitive, then this event should not be included in the + * output log. The system waits to output until the containing word is known. + * + * @param keys an array containing a descriptive name for the event, followed by the keys + * @param values an array of values, either a String or Number. length should be one + * less than the keys array + */ + private synchronized void enqueuePotentiallyPrivateEvent(final String[] keys, + final Object[] values) { + assert values.length + 1 == keys.length; + mCurrentLogUnit.addLogAtom(keys, values, true); + } + + /** + * Buffer a research log event, flaggint it as not privacy-sensitive. + * + * This event contains no potentially private information. Even if the word that this event + * is privacy-sensitive, this event can still safely be sent to the output log. The system + * waits until the containing word is known so that this event can be written in the proper + * temporal order with other events that may be privacy sensitive. + * + * @param keys an array containing a descriptive name for the event, followed by the keys + * @param values an array of values, either a String or Number. length should be one + * less than the keys array + */ + private synchronized void enqueueEvent(final String[] keys, final Object[] values) { + assert values.length + 1 == keys.length; + mCurrentLogUnit.addLogAtom(keys, values, false); + } + + // Used to track how often words are logged. Too-frequent logging can leak + // semantics, disclosing private data. + /* package for test */ static class LoggingFrequencyState { + private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; + private int mWordsRemainingToSkip; + private final int mFrequency; + + /** + * Tracks how often words may be uploaded. + * + * @param frequency 1=Every word, 2=Every other word, etc. + */ + public LoggingFrequencyState(int frequency) { + mFrequency = frequency; + mWordsRemainingToSkip = mFrequency; + } + + public void onWordLogged() { + mWordsRemainingToSkip = mFrequency; + } + + public void onWordNotLogged() { + if (mWordsRemainingToSkip > 1) { + mWordsRemainingToSkip--; } } - if (prefs != null) { - sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); - prefs.registerOnSharedPreferenceChangeListener(this); + + public boolean isSafeToLog() { + return mWordsRemainingToSkip <= 1; + } + } + + /* package for test */ LoggingFrequencyState mLoggingFrequencyState = + new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); + + /* package for test */ boolean isPrivacyThreat(String word) { + // Current checks: + // - Word not in dictionary + // - Word contains numbers + // - Privacy-safe word not logged recently + if (TextUtils.isEmpty(word)) { + return false; + } + if (!mLoggingFrequencyState.isSafeToLog()) { + return true; + } + final int length = word.length(); + boolean hasLetter = false; + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = Character.codePointAt(word, i); + if (Character.isDigit(codePoint)) { + return true; + } + if (Character.isLetter(codePoint)) { + hasLetter = true; + break; // Word may contain digits, but will only be allowed if in the dictionary. + } + } + if (hasLetter) { + if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) { + mDictionary = mSuggest.getMainDictionary(); + } + if (mDictionary == null) { + // Can't access dictionary. Assume privacy threat. + return true; + } + return !(mDictionary.isValidWord(word)); + } + // No letters, no numbers. Punctuation, space, or something else. + return false; + } + + private void onWordComplete(String word) { + final boolean isPrivacyThreat = isPrivacyThreat(word); + flushEventQueue(isPrivacyThreat); + if (isPrivacyThreat) { + mLoggingFrequencyState.onWordNotLogged(); + } else { + mLoggingFrequencyState.onWordLogged(); } } /** - * Represents a category of logging events that share the same subfield structure. + * Write out enqueued LogEvents to the log, possibly dropping privacy sensitive events. */ - private static enum LogGroup { - MOTION_EVENT("m"), - KEY("k"), - CORRECTION("c"), - STATE_CHANGE("s"), - UNSTRUCTURED("u"); - - private final String mLogString; - - private LogGroup(final String logString) { - mLogString = logString; - } - } - - public void logMotionEvent(final int action, final long eventTime, final int id, - final int x, final int y, final float size, final float pressure) { - final String eventTag; - switch (action) { - case MotionEvent.ACTION_CANCEL: eventTag = "[Cancel]"; break; - case MotionEvent.ACTION_UP: eventTag = "[Up]"; break; - case MotionEvent.ACTION_DOWN: eventTag = "[Down]"; break; - case MotionEvent.ACTION_POINTER_UP: eventTag = "[PointerUp]"; break; - case MotionEvent.ACTION_POINTER_DOWN: eventTag = "[PointerDown]"; break; - case MotionEvent.ACTION_MOVE: eventTag = "[Move]"; break; - case MotionEvent.ACTION_OUTSIDE: eventTag = "[Outside]"; break; - default: eventTag = "[Action" + action + "]"; break; - } - if (!TextUtils.isEmpty(eventTag)) { - final StringBuilder sb = new StringBuilder(); - sb.append(eventTag); - sb.append('\t'); sb.append(eventTime); - sb.append('\t'); sb.append(id); - sb.append('\t'); sb.append(x); - sb.append('\t'); sb.append(y); - sb.append('\t'); sb.append(size); - sb.append('\t'); sb.append(pressure); - write(LogGroup.MOTION_EVENT, sb.toString()); - } - } - - public void logKeyEvent(final int code, final int x, final int y) { - final StringBuilder sb = new StringBuilder(); - sb.append(Keyboard.printableCode(code)); - sb.append('\t'); sb.append(x); - sb.append('\t'); sb.append(y); - write(LogGroup.KEY, sb.toString()); - } - - public void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final StringBuilder sb = new StringBuilder(); - sb.append(subgroup); - sb.append('\t'); sb.append(before); - sb.append('\t'); sb.append(after); - sb.append('\t'); sb.append(position); - write(LogGroup.CORRECTION, sb.toString()); - } - - public void logStateChange(final String subgroup, final String details) { - write(LogGroup.STATE_CHANGE, subgroup + "\t" + details); - } - - public static class UnsLogGroup { - private static final boolean DEFAULT_ENABLED = true; - - private static final boolean KEYBOARDSTATE_ONCANCELINPUT_ENABLED = DEFAULT_ENABLED; - private static final boolean KEYBOARDSTATE_ONCODEINPUT_ENABLED = DEFAULT_ENABLED; - private static final boolean KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED = DEFAULT_ENABLED; - private static final boolean KEYBOARDSTATE_ONPRESSKEY_ENABLED = DEFAULT_ENABLED; - private static final boolean KEYBOARDSTATE_ONRELEASEKEY_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_COMMITTEXT_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_DELETESURROUNDINGTEXT_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_ONDISPLAYCOMPLETIONS_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_ONUPDATESELECTION_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_PERFORMEDITORACTION_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED - = DEFAULT_ENABLED; - private static final boolean LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_PICKSUGGESTIONMANUALLY_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_REVERTCOMMIT_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED - = DEFAULT_ENABLED; - private static final boolean LATINIME_REVERTSWAPPUNCTUATION_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_SENDKEYCODEPOINT_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED - = DEFAULT_ENABLED; - private static final boolean LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED = DEFAULT_ENABLED; - private static final boolean LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED - = DEFAULT_ENABLED; - private static final boolean LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED = DEFAULT_ENABLED; - private static final boolean POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED - = DEFAULT_ENABLED; - private static final boolean POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED - = DEFAULT_ENABLED; - private static final boolean - POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED - = DEFAULT_ENABLED; - private static final boolean POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED = DEFAULT_ENABLED; - private static final boolean POINTERTRACKER_ONDOWNEVENT_ENABLED = DEFAULT_ENABLED; - private static final boolean POINTERTRACKER_ONMOVEEVENT_ENABLED = DEFAULT_ENABLED; - private static final boolean SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED - = DEFAULT_ENABLED; - private static final boolean SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED = DEFAULT_ENABLED; - } - - public static void logUnstructured(String logGroup, final String details) { - // TODO: improve performance by making entire class static and/or implementing natively - getInstance().write(LogGroup.UNSTRUCTURED, logGroup + "\t" + details); - } - - private void write(final LogGroup logGroup, final String log) { - // TODO: rewrite in native for better performance - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - final long currentTime = System.currentTimeMillis(); - final long upTime = SystemClock.uptimeMillis(); - final StringBuilder builder = new StringBuilder(); - builder.append(currentTime); - builder.append('\t'); builder.append(upTime); - builder.append('\t'); builder.append(logGroup.mLogString); - builder.append('\t'); builder.append(log); - builder.append('\n'); - if (DEBUG) { - Log.d(TAG, "Write: " + '[' + logGroup.mLogString + ']' + log); - } - final String s = builder.toString(); - if (mLogFileManager.append(s)) { - // success - } else { - if (DEBUG) { - Log.w(TAG, "Unable to write to log."); + /* package for test */ synchronized void flushEventQueue( + boolean removePotentiallyPrivateEvents) { + if (isAllowedToLog()) { + mCurrentLogUnit.setRemovePotentiallyPrivateEvents(removePotentiallyPrivateEvents); + mLoggingHandler.post(mCurrentLogUnit); + } + mCurrentLogUnit = new LogUnit(); + } + + private synchronized void outputEvent(final String[] keys, final Object[] values) { + try { + mJsonWriter.beginObject(); + mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); + mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis()); + mJsonWriter.name(EVENT_TYPE_KEY).value(keys[0]); + final int length = values.length; + for (int i = 0; i < length; i++) { + mJsonWriter.name(keys[i + 1]); + Object value = values[i]; + if (value instanceof String) { + mJsonWriter.value((String) value); + } else if (value instanceof Number) { + mJsonWriter.value((Number) value); + } else if (value instanceof Boolean) { + mJsonWriter.value((Boolean) value); + } else if (value instanceof CompletionInfo[]) { + CompletionInfo[] ci = (CompletionInfo[]) value; + mJsonWriter.beginArray(); + for (int j = 0; j < ci.length; j++) { + mJsonWriter.value(ci[j].toString()); } - // perhaps logfile was deleted. try to recreate and relog. - try { - mLogFileManager.createLogFile(PreferenceManager - .getDefaultSharedPreferences(mIms)); - mLogFileManager.append(s); - } catch (IOException e) { - e.printStackTrace(); + mJsonWriter.endArray(); + } else if (value instanceof SharedPreferences) { + SharedPreferences prefs = (SharedPreferences) value; + mJsonWriter.beginObject(); + for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) { + mJsonWriter.name(entry.getKey()); + final Object innerValue = entry.getValue(); + if (innerValue == null) { + mJsonWriter.nullValue(); + } else if (innerValue instanceof Boolean) { + mJsonWriter.value((Boolean) innerValue); + } else if (innerValue instanceof Number) { + mJsonWriter.value((Number) innerValue); + } else { + mJsonWriter.value(innerValue.toString()); + } + } + mJsonWriter.endObject(); + } else if (value instanceof Key[]) { + Key[] keyboardKeys = (Key[]) value; + mJsonWriter.beginArray(); + for (Key keyboardKey : keyboardKeys) { + mJsonWriter.beginObject(); + mJsonWriter.name("code").value(keyboardKey.mCode); + mJsonWriter.name("altCode").value(keyboardKey.mAltCode); + mJsonWriter.name("x").value(keyboardKey.mX); + mJsonWriter.name("y").value(keyboardKey.mY); + mJsonWriter.name("w").value(keyboardKey.mWidth); + mJsonWriter.name("h").value(keyboardKey.mHeight); + mJsonWriter.endObject(); } + mJsonWriter.endArray(); + } else if (value instanceof SuggestedWords) { + SuggestedWords words = (SuggestedWords) value; + mJsonWriter.beginObject(); + mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); + mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect); + mJsonWriter.name("isPunctuationSuggestions") + .value(words.mIsPunctuationSuggestions); + mJsonWriter.name("isObsoleteSuggestions") + .value(words.mIsObsoleteSuggestions); + mJsonWriter.name("isPrediction") + .value(words.mIsPrediction); + mJsonWriter.name("words"); + mJsonWriter.beginArray(); + final int size = words.size(); + for (int j = 0; j < size; j++) { + SuggestedWordInfo wordInfo = words.getWordInfo(j); + mJsonWriter.value(wordInfo.toString()); + } + mJsonWriter.endArray(); + mJsonWriter.endObject(); + } else if (value == null) { + mJsonWriter.nullValue(); + } else { + Log.w(TAG, "Unrecognized type to be logged: " + + (value == null ? "<null>" : value.getClass().getName())); + mJsonWriter.nullValue(); } } - }); + mJsonWriter.endObject(); + } catch (IOException e) { + e.printStackTrace(); + Log.w(TAG, "Error in JsonWriter; disabling logging"); + try { + mJsonWriter.close(); + } catch (IllegalStateException e1) { + // assume that this is just the json not being terminated properly. + // ignore + } catch (IOException e1) { + e1.printStackTrace(); + } finally { + mJsonWriter = NULL_JSON_WRITER; + } + } } - public void clearAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - if (DEBUG) { - Log.d(TAG, "Delete log file."); + private static class LogUnit implements Runnable { + private final List<String[]> mKeysList = new ArrayList<String[]>(); + private final List<Object[]> mValuesList = new ArrayList<Object[]>(); + private final List<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>(); + private boolean mRemovePotentiallyPrivateEvents = true; + + private void addLogAtom(final String[] keys, final Object[] values, + final Boolean isPotentiallyPrivate) { + mKeysList.add(keys); + mValuesList.add(values); + mIsPotentiallyPrivate.add(isPotentiallyPrivate); + } + + void setRemovePotentiallyPrivateEvents(boolean removePotentiallyPrivateEvents) { + mRemovePotentiallyPrivateEvents = removePotentiallyPrivateEvents; + } + + @Override + public void run() { + final int numAtoms = mKeysList.size(); + for (int atomIndex = 0; atomIndex < numAtoms; atomIndex++) { + if (mRemovePotentiallyPrivateEvents && mIsPotentiallyPrivate.get(atomIndex)) { + continue; } - mLogFileManager.reset(); + final String[] keys = mKeysList.get(atomIndex); + final Object[] values = mValuesList.get(atomIndex); + ResearchLogger.getInstance().outputEvent(keys, values); } - }); + } } - /* package */ LogFileManager getLogFileManager() { - return mLogFileManager; + private static int scrubDigitFromCodePoint(int codePoint) { + return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint; } - @Override - public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { - if (key == null || prefs == null) { - return; + /* package for test */ static String scrubDigitsFromString(String s) { + StringBuilder sb = null; + final int length = s.length(); + for (int i = 0; i < length; i = s.offsetByCodePoints(i, 1)) { + final int codePoint = Character.codePointAt(s, i); + if (Character.isDigit(codePoint)) { + if (sb == null) { + sb = new StringBuilder(length); + sb.append(s.substring(0, i)); + } + sb.appendCodePoint(DIGIT_REPLACEMENT_CODEPOINT); + } else { + if (sb != null) { + sb.appendCodePoint(codePoint); + } + } } - sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); - } - - public static void keyboardState_onCancelInput(final boolean isSinglePointer, - final KeyboardState keyboardState) { - if (UnsLogGroup.KEYBOARDSTATE_ONCANCELINPUT_ENABLED) { - final String s = "onCancelInput: single=" + isSinglePointer + " " + keyboardState; - logUnstructured("KeyboardState_onCancelInput", s); + if (sb == null) { + return s; + } else { + return sb.toString(); } } - public static void keyboardState_onCodeInput( - final int code, final boolean isSinglePointer, final int autoCaps, - final KeyboardState keyboardState) { - if (UnsLogGroup.KEYBOARDSTATE_ONCODEINPUT_ENABLED) { - final String s = "onCodeInput: code=" + Keyboard.printableCode(code) - + " single=" + isSinglePointer - + " autoCaps=" + autoCaps + " " + keyboardState; - logUnstructured("KeyboardState_onCodeInput", s); + private String scrubWord(String word) { + if (mDictionary == null) { + return WORD_REPLACEMENT_STRING; + } + if (mDictionary.isValidWord(word)) { + return word; } + return WORD_REPLACEMENT_STRING; } - public static void keyboardState_onLongPressTimeout(final int code, - final KeyboardState keyboardState) { - if (UnsLogGroup.KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED) { - final String s = "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " " - + keyboardState; - logUnstructured("KeyboardState_onLongPressTimeout", s); + private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = { + "LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size", + "pressure" + }; + public static void latinKeyboardView_processMotionEvent(final MotionEvent me, final int action, + final long eventTime, final int index, final int id, final int x, final int y) { + if (me != null) { + final String actionString; + switch (action) { + case MotionEvent.ACTION_CANCEL: actionString = "CANCEL"; break; + case MotionEvent.ACTION_UP: actionString = "UP"; break; + case MotionEvent.ACTION_DOWN: actionString = "DOWN"; break; + case MotionEvent.ACTION_POINTER_UP: actionString = "POINTER_UP"; break; + case MotionEvent.ACTION_POINTER_DOWN: actionString = "POINTER_DOWN"; break; + case MotionEvent.ACTION_MOVE: actionString = "MOVE"; break; + case MotionEvent.ACTION_OUTSIDE: actionString = "OUTSIDE"; break; + default: actionString = "ACTION_" + action; break; + } + final float size = me.getSize(index); + final float pressure = me.getPressure(index); + final Object[] values = { + actionString, eventTime, id, x, y, size, pressure + }; + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT, values); } } - public static void keyboardState_onPressKey(final int code, - final KeyboardState keyboardState) { - if (UnsLogGroup.KEYBOARDSTATE_ONPRESSKEY_ENABLED) { - final String s = "onPressKey: code=" + Keyboard.printableCode(code) + " " - + keyboardState; - logUnstructured("KeyboardState_onPressKey", s); - } + private static final String[] EVENTKEYS_LATINIME_ONCODEINPUT = { + "LatinIMEOnCodeInput", "code", "x", "y" + }; + public static void latinIME_onCodeInput(final int code, final int x, final int y) { + final Object[] values = { + Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } - public static void keyboardState_onReleaseKey(final KeyboardState keyboardState, final int code, - final boolean withSliding) { - if (UnsLogGroup.KEYBOARDSTATE_ONRELEASEKEY_ENABLED) { - final String s = "onReleaseKey: code=" + Keyboard.printableCode(code) - + " sliding=" + withSliding + " " + keyboardState; - logUnstructured("KeyboardState_onReleaseKey", s); - } + private static final String[] EVENTKEYS_CORRECTION = { + "LogCorrection", "subgroup", "before", "after", "position" + }; + public static void logCorrection(final String subgroup, final String before, final String after, + final int position) { + final Object[] values = { + subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); } + private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { + "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" + }; public static void latinIME_commitCurrentAutoCorrection(final String typedWord, final String autoCorrection) { - if (UnsLogGroup.LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED) { - if (typedWord.equals(autoCorrection)) { - getInstance().logCorrection("[----]", typedWord, autoCorrection, -1); - } else { - getInstance().logCorrection("[Auto]", typedWord, autoCorrection, -1); - } - } + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); } + private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { + "LatinIMECommitText", "typedWord" + }; public static void latinIME_commitText(final CharSequence typedWord) { - if (UnsLogGroup.LATINIME_COMMITTEXT_ENABLED) { - logUnstructured("LatinIME_commitText", typedWord.toString()); - } + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); + researchLogger.onWordComplete(scrubbedWord); } + private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { + "LatinIMEDeleteSurroundingText", "length" + }; public static void latinIME_deleteSurroundingText(final int length) { - if (UnsLogGroup.LATINIME_DELETESURROUNDINGTEXT_ENABLED) { - logUnstructured("LatinIME_deleteSurroundingText", String.valueOf(length)); - } + final Object[] values = { + length + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); } + private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { + "LatinIMEDoubleSpaceAutoPeriod" + }; public static void latinIME_doubleSpaceAutoPeriod() { - if (UnsLogGroup.LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED) { - logUnstructured("LatinIME_doubleSpaceAutoPeriod", ""); - } + getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); } + private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { + "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" + }; public static void latinIME_onDisplayCompletions( final CompletionInfo[] applicationSpecifiedCompletions) { - if (UnsLogGroup.LATINIME_ONDISPLAYCOMPLETIONS_ENABLED) { - final StringBuilder builder = new StringBuilder(); - builder.append("Received completions:"); - if (applicationSpecifiedCompletions != null) { - for (int i = 0; i < applicationSpecifiedCompletions.length; i++) { - builder.append(" #"); - builder.append(i); - builder.append(": "); - builder.append(applicationSpecifiedCompletions[i]); - builder.append("\n"); + final Object[] values = { + applicationSpecifiedCompletions + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS, + values); + } + + /* package */ static boolean getAndClearLatinIMEExpectingUpdateSelection() { + boolean returnValue = sLatinIMEExpectingUpdateSelection; + sLatinIMEExpectingUpdateSelection = false; + return returnValue; + } + + private static final String[] EVENTKEYS_LATINIME_ONWINDOWHIDDEN = { + "LatinIMEOnWindowHidden", "isTextTruncated", "text" + }; + public static void latinIME_onWindowHidden(final int savedSelectionStart, + final int savedSelectionEnd, final InputConnection ic) { + if (ic != null) { + ic.beginBatchEdit(); + ic.performContextMenuAction(android.R.id.selectAll); + CharSequence charSequence = ic.getSelectedText(0); + ic.setSelection(savedSelectionStart, savedSelectionEnd); + ic.endBatchEdit(); + sLatinIMEExpectingUpdateSelection = true; + final Object[] values = new Object[2]; + if (OUTPUT_ENTIRE_BUFFER) { + if (TextUtils.isEmpty(charSequence)) { + values[0] = false; + values[1] = ""; + } else { + if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) { + int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE; + // do not cut in the middle of a supplementary character + final char c = charSequence.charAt(length - 1); + if (Character.isHighSurrogate(c)) { + length--; + } + final CharSequence truncatedCharSequence = charSequence.subSequence(0, + length); + values[0] = true; + values[1] = truncatedCharSequence.toString(); + } else { + values[0] = false; + values[1] = charSequence.toString(); + } } + } else { + values[0] = true; + values[1] = ""; } - logUnstructured("LatinIME_onDisplayCompletions", builder.toString()); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); + researchLogger.flushEventQueue(true); // Play it safe. Remove privacy-sensitive events. } } + private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { + "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", + "fieldId", "display", "model", "prefs", "outputFormatVersion" + }; public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, final SharedPreferences prefs) { - if (UnsLogGroup.LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED) { - final StringBuilder builder = new StringBuilder(); - builder.append("onStartInputView: editorInfo:"); - builder.append("\tinputType="); - builder.append(Integer.toHexString(editorInfo.inputType)); - builder.append("\timeOptions="); - builder.append(Integer.toHexString(editorInfo.imeOptions)); - builder.append("\tdisplay="); builder.append(Build.DISPLAY); - builder.append("\tmodel="); builder.append(Build.MODEL); - for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) { - builder.append("\t" + entry.getKey()); - Object value = entry.getValue(); - builder.append("=" + ((value == null) ? "<null>" : value.toString())); - } - logUnstructured("LatinIME_onStartInputViewInternal", builder.toString()); + if (editorInfo != null) { + final Object[] values = { + getUUID(prefs), editorInfo.packageName, Integer.toHexString(editorInfo.inputType), + Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, Build.DISPLAY, + Build.MODEL, prefs, OUTPUT_FORMAT_VERSION + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values); } } + private static String getUUID(final SharedPreferences prefs) { + String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null); + if (null == uuidString) { + UUID uuid = UUID.randomUUID(); + uuidString = uuid.toString(); + Editor editor = prefs.edit(); + editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString); + editor.apply(); + } + return uuidString; + } + + private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = { + "LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart", + "oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd", + "expectingUpdateSelection", "expectingUpdateSelectionFromLogger", "context" + }; public static void latinIME_onUpdateSelection(final int lastSelectionStart, final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd, final int newSelStart, final int newSelEnd, final int composingSpanStart, - final int composingSpanEnd) { - if (UnsLogGroup.LATINIME_ONUPDATESELECTION_ENABLED) { - final String s = "onUpdateSelection: oss=" + oldSelStart - + ", ose=" + oldSelEnd - + ", lss=" + lastSelectionStart - + ", lse=" + lastSelectionEnd - + ", nss=" + newSelStart - + ", nse=" + newSelEnd - + ", cs=" + composingSpanStart - + ", ce=" + composingSpanEnd; - logUnstructured("LatinIME_onUpdateSelection", s); + final int composingSpanEnd, final boolean expectingUpdateSelection, + final boolean expectingUpdateSelectionFromLogger, + final RichInputConnection connection) { + String word = ""; + if (connection != null) { + Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); + if (range != null) { + word = range.mWord; + } } + final ResearchLogger researchLogger = getInstance(); + final String scrubbedWord = researchLogger.scrubWord(word); + final Object[] values = { + lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, + newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection, + expectingUpdateSelectionFromLogger, scrubbedWord + }; + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } + private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { + "LatinIMEPerformEditorAction", "imeActionNext" + }; public static void latinIME_performEditorAction(final int imeActionNext) { - if (UnsLogGroup.LATINIME_PERFORMEDITORACTION_ENABLED) { - logUnstructured("LatinIME_performEditorAction", String.valueOf(imeActionNext)); - } + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); } + private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { + "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" + }; public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence text, int x, int y) { - if (UnsLogGroup.LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED) { - final String s = String.valueOf(index) + '\t' + text + '\t' + x + '\t' + y; - logUnstructured("LatinIME_pickApplicationSpecifiedCompletion", s); - } + final CharSequence cs, int x, int y) { + final Object[] values = { + index, cs, x, y + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); } + private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { + "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" + }; public static void latinIME_pickSuggestionManually(final String replacedWord, final int index, CharSequence suggestion, int x, int y) { - if (UnsLogGroup.LATINIME_PICKSUGGESTIONMANUALLY_ENABLED) { - final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y; - logUnstructured("LatinIME_pickSuggestionManually", s); - } + final Object[] values = { + scrubDigitsFromString(replacedWord), index, suggestion == null ? null : + scrubDigitsFromString(suggestion.toString()), x, y + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, + values); } + private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = { + "LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y" + }; public static void latinIME_punctuationSuggestion(final int index, final CharSequence suggestion, int x, int y) { - if (UnsLogGroup.LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED) { - final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y; - logUnstructured("LatinIME_pickPunctuationSuggestion", s); - } + final Object[] values = { + index, suggestion, x, y + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } + private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { + "LatinIMERevertDoubleSpaceWhileInBatchEdit" + }; public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - if (UnsLogGroup.LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED) { - logUnstructured("LatinIME_revertDoubleSpaceWhileInBatchEdit", ""); - } + getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, + EVENTKEYS_NULLVALUES); } + private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { + "LatinIMERevertSwapPunctuation" + }; public static void latinIME_revertSwapPunctuation() { - if (UnsLogGroup.LATINIME_REVERTSWAPPUNCTUATION_ENABLED) { - logUnstructured("LatinIME_revertSwapPunctuation", ""); - } + getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); } + private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { + "LatinIMESendKeyCodePoint", "code" + }; public static void latinIME_sendKeyCodePoint(final int code) { - if (UnsLogGroup.LATINIME_SENDKEYCODEPOINT_ENABLED) { - logUnstructured("LatinIME_sendKeyCodePoint", String.valueOf(code)); - } + final Object[] values = { + Keyboard.printableCode(scrubDigitFromCodePoint(code)) + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { + "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" + }; public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - if (UnsLogGroup.LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED) { - logUnstructured("latinIME_swapSwapperAndSpaceWhileInBatchEdit", ""); - } - } - - public static void latinIME_switchToKeyboardView() { - if (UnsLogGroup.LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED) { - final String s = "Switch to keyboard view."; - logUnstructured("LatinIME_switchToKeyboardView", s); - } + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, + EVENTKEYS_NULLVALUES); } + private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS = { + "LatinKeyboardViewOnLongPress" + }; public static void latinKeyboardView_onLongPress() { - if (UnsLogGroup.LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED) { - final String s = "long press detected"; - logUnstructured("LatinKeyboardView_onLongPress", s); - } - } - - public static void latinKeyboardView_processMotionEvent(MotionEvent me, int action, - long eventTime, int index, int id, int x, int y) { - if (UnsLogGroup.LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED) { - final float size = me.getSize(index); - final float pressure = me.getPressure(index); - if (action != MotionEvent.ACTION_MOVE) { - getInstance().logMotionEvent(action, eventTime, id, x, y, size, pressure); - } - } + getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES); } + private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD = { + "LatinKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width", + "modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey", + "passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled", + "isMultiLine", "tw", "th", "keys" + }; public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) { - if (UnsLogGroup.LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED) { - StringBuilder builder = new StringBuilder(); - builder.append("id="); - builder.append(keyboard.mId); - builder.append("\tw="); - builder.append(keyboard.mOccupiedWidth); - builder.append("\th="); - builder.append(keyboard.mOccupiedHeight); - builder.append("\tkeys=["); - boolean first = true; - for (Key key : keyboard.mKeys) { - if (first) { - first = false; - } else { - builder.append(","); - } - builder.append("{code:"); - builder.append(key.mCode); - builder.append(",altCode:"); - builder.append(key.mAltCode); - builder.append(",x:"); - builder.append(key.mX); - builder.append(",y:"); - builder.append(key.mY); - builder.append(",w:"); - builder.append(key.mWidth); - builder.append(",h:"); - builder.append(key.mHeight); - builder.append("}"); - } - builder.append("]"); - logUnstructured("LatinKeyboardView_setKeyboard", builder.toString()); + if (keyboard != null) { + final KeyboardId kid = keyboard.mId; + final boolean isPasswordView = kid.passwordInput(); + final Object[] values = { + KeyboardId.elementIdToName(kid.mElementId), + kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), + kid.mOrientation, + kid.mWidth, + KeyboardId.modeName(kid.mMode), + kid.imeAction(), + kid.navigateNext(), + kid.navigatePrevious(), + kid.mClobberSettingsKey, + isPasswordView, + kid.mShortcutKeyEnabled, + kid.mHasShortcutKey, + kid.mLanguageSwitchKeyEnabled, + kid.isMultiLine(), + keyboard.mOccupiedWidth, + keyboard.mOccupiedHeight, + keyboard.mKeys + }; + getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD, values); + getInstance().setIsPasswordView(isPasswordView); } } + private static final String[] EVENTKEYS_LATINIME_REVERTCOMMIT = { + "LatinIMERevertCommit", "originallyTypedWord" + }; public static void latinIME_revertCommit(final String originallyTypedWord) { - if (UnsLogGroup.LATINIME_REVERTCOMMIT_ENABLED) { - logUnstructured("LatinIME_revertCommit", originallyTypedWord); - } + final Object[] values = { + originallyTypedWord + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_REVERTCOMMIT, values); } + private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT = { + "PointerTrackerCallListenerOnCancelInput" + }; public static void pointerTracker_callListenerOnCancelInput() { - final String s = "onCancelInput"; - if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED) { - logUnstructured("PointerTracker_callListenerOnCancelInput", s); - } + getInstance().enqueueEvent(EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT, + EVENTKEYS_NULLVALUES); } + private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT = { + "PointerTrackerCallListenerOnCodeInput", "code", "outputText", "x", "y", + "ignoreModifierKey", "altersCode", "isEnabled" + }; public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x, final int y, final boolean ignoreModifierKey, final boolean altersCode, final int code) { - if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED) { - final String s = "onCodeInput: " + Keyboard.printableCode(code) - + " text=" + key.mOutputText + " x=" + x + " y=" + y - + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode - + " enabled=" + key.isEnabled(); - logUnstructured("PointerTracker_callListenerOnCodeInput", s); - } - } - - public static void pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange( - final Key key, final boolean ignoreModifierKey) { - if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED) { - final String s = "onPress : " + KeyDetector.printableCode(key) - + " ignoreModifier=" + ignoreModifierKey - + " enabled=" + key.isEnabled(); - logUnstructured("PointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange", s); + if (key != null) { + CharSequence outputText = key.mOutputText; + final Object[] values = { + Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null + : scrubDigitsFromString(outputText.toString()), + x, y, ignoreModifierKey, altersCode, key.isEnabled() + }; + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT, values); } } + private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE = { + "PointerTrackerCallListenerOnRelease", "code", "withSliding", "ignoreModifierKey", + "isEnabled" + }; public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode, final boolean withSliding, final boolean ignoreModifierKey) { - if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED) { - final String s = "onRelease : " + Keyboard.printableCode(primaryCode) - + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey - + " enabled="+ key.isEnabled(); - logUnstructured("PointerTracker_callListenerOnRelease", s); + if (key != null) { + final Object[] values = { + Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding, + ignoreModifierKey, key.isEnabled() + }; + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE, values); } } + private static final String[] EVENTKEYS_POINTERTRACKER_ONDOWNEVENT = { + "PointerTrackerOnDownEvent", "deltaT", "distanceSquared" + }; public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) { - if (UnsLogGroup.POINTERTRACKER_ONDOWNEVENT_ENABLED) { - final String s = "onDownEvent: ignore potential noise: time=" + deltaT - + " distance=" + distanceSquared; - logUnstructured("PointerTracker_onDownEvent", s); - } + final Object[] values = { + deltaT, distanceSquared + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONDOWNEVENT, values); } + private static final String[] EVENTKEYS_POINTERTRACKER_ONMOVEEVENT = { + "PointerTrackerOnMoveEvent", "x", "y", "lastX", "lastY" + }; public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX, final int lastY) { - if (UnsLogGroup.POINTERTRACKER_ONMOVEEVENT_ENABLED) { - final String s = String.format("onMoveEvent: sudden move is translated to " - + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y); - logUnstructured("PointerTracker_onMoveEvent", s); - } + final Object[] values = { + x, y, lastX, lastY + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } + private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { + "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" + }; public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) { - if (UnsLogGroup.SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED) { - final String s = "onTouchEvent: ignore sudden jump " + me; - logUnstructured("SuddenJumpingTouchEventHandler_onTouchEvent", s); + if (me != null) { + final Object[] values = { + me.toString() + }; + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, values); } } - public static void suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords) { - if (UnsLogGroup.SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED) { - logUnstructured("SuggestionsView_setSuggestions", mSuggestedWords.toString()); + private static final String[] EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS = { + "SuggestionsViewSetSuggestions", "suggestedWords" + }; + public static void suggestionsView_setSuggestions(final SuggestedWords suggestedWords) { + if (suggestedWords != null) { + final Object[] values = { + suggestedWords + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS, + values); } } -}
\ No newline at end of file + + private static final String[] EVENTKEYS_USER_TIMESTAMP = { + "UserTimestamp" + }; + public void userTimestamp() { + getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); + } +} diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java new file mode 100644 index 000000000..5786978a8 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.inputmethodservice.InputMethodService; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; + +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.define.ProductionFlag; + +import java.util.regex.Pattern; + +/** + * Wrapper for InputConnection to simplify interaction + */ +public class RichInputConnection { + private static final String TAG = RichInputConnection.class.getSimpleName(); + private static final boolean DBG = false; + // Provision for a long word pair and a separator + private static final int LOOKBACK_CHARACTER_NUM = BinaryDictionary.MAX_WORD_LENGTH * 2 + 1; + private static final Pattern spaceRegex = Pattern.compile("\\s+"); + private static final int INVALID_CURSOR_POSITION = -1; + + private final InputMethodService mParent; + InputConnection mIC; + int mNestLevel; + public RichInputConnection(final InputMethodService parent) { + mParent = parent; + mIC = null; + mNestLevel = 0; + } + + public void beginBatchEdit() { + if (++mNestLevel == 1) { + mIC = mParent.getCurrentInputConnection(); + if (null != mIC) mIC.beginBatchEdit(); + } else { + if (DBG) { + throw new RuntimeException("Nest level too deep"); + } else { + Log.e(TAG, "Nest level too deep : " + mNestLevel); + } + } + } + public void endBatchEdit() { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + } + + private void checkBatchEdit() { + if (mNestLevel != 1) { + // TODO: exception instead + Log.e(TAG, "Batch edit level incorrect : " + mNestLevel); + Log.e(TAG, Utils.getStackTrace(4)); + } + } + + public void finishComposingText() { + checkBatchEdit(); + if (null != mIC) mIC.finishComposingText(); + } + + public void commitText(final CharSequence text, final int i) { + checkBatchEdit(); + if (null != mIC) mIC.commitText(text, i); + } + + public int getCursorCapsMode(final int inputType) { + mIC = mParent.getCurrentInputConnection(); + if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF; + return mIC.getCursorCapsMode(inputType); + } + + public CharSequence getTextBeforeCursor(final int i, final int j) { + mIC = mParent.getCurrentInputConnection(); + if (null != mIC) return mIC.getTextBeforeCursor(i, j); + return null; + } + + public CharSequence getTextAfterCursor(final int i, final int j) { + mIC = mParent.getCurrentInputConnection(); + if (null != mIC) return mIC.getTextAfterCursor(i, j); + return null; + } + + public void deleteSurroundingText(final int i, final int j) { + checkBatchEdit(); + if (null != mIC) mIC.deleteSurroundingText(i, j); + } + + public void performEditorAction(final int actionId) { + mIC = mParent.getCurrentInputConnection(); + if (null != mIC) mIC.performEditorAction(actionId); + } + + public void sendKeyEvent(final KeyEvent keyEvent) { + checkBatchEdit(); + if (null != mIC) mIC.sendKeyEvent(keyEvent); + } + + public void setComposingText(final CharSequence text, final int i) { + checkBatchEdit(); + if (null != mIC) mIC.setComposingText(text, i); + } + + public void setSelection(final int from, final int to) { + checkBatchEdit(); + if (null != mIC) mIC.setSelection(from, to); + } + + public void commitCorrection(final CorrectionInfo correctionInfo) { + checkBatchEdit(); + if (null != mIC) mIC.commitCorrection(correctionInfo); + } + + public void commitCompletion(final CompletionInfo completionInfo) { + checkBatchEdit(); + if (null != mIC) mIC.commitCompletion(completionInfo); + } + + public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { + mIC = mParent.getCurrentInputConnection(); + if (null == mIC) return null; + final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); + return getNthPreviousWord(prev, sentenceSeperators, n); + } + + /** + * Represents a range of text, relative to the current cursor position. + */ + public static class Range { + /** Characters before selection start */ + public final int mCharsBefore; + + /** + * Characters after selection start, including one trailing word + * separator. + */ + public final int mCharsAfter; + + /** The actual characters that make up a word */ + public final String mWord; + + public Range(int charsBefore, int charsAfter, String word) { + if (charsBefore < 0 || charsAfter < 0) { + throw new IndexOutOfBoundsException(); + } + this.mCharsBefore = charsBefore; + this.mCharsAfter = charsAfter; + this.mWord = word; + } + } + + private static boolean isSeparator(int code, String sep) { + return sep.indexOf(code) != -1; + } + + // Get the nth word before cursor. n = 1 retrieves the word immediately before the cursor, + // n = 2 retrieves the word before that, and so on. This splits on whitespace only. + // Also, it won't return words that end in a separator (if the nth word before the cursor + // ends in a separator, it returns null). + // Example : + // (n = 1) "abc def|" -> def + // (n = 1) "abc def |" -> def + // (n = 1) "abc def. |" -> null + // (n = 1) "abc def . |" -> null + // (n = 2) "abc def|" -> abc + // (n = 2) "abc def |" -> abc + // (n = 2) "abc def. |" -> abc + // (n = 2) "abc def . |" -> def + // (n = 2) "abc|" -> null + // (n = 2) "abc |" -> null + // (n = 2) "abc. def|" -> null + public static CharSequence getNthPreviousWord(final CharSequence prev, + final String sentenceSeperators, final int n) { + if (prev == null) return null; + String[] w = spaceRegex.split(prev); + + // If we can't find n words, or we found an empty word, return null. + if (w.length < n || w[w.length - n].length() <= 0) return null; + + // If ends in a separator, return null + char lastChar = w[w.length - n].charAt(w[w.length - n].length() - 1); + if (sentenceSeperators.contains(String.valueOf(lastChar))) return null; + + return w[w.length - n]; + } + + /** + * @param separators characters which may separate words + * @return the word that surrounds the cursor, including up to one trailing + * separator. For example, if the field contains "he|llo world", where | + * represents the cursor, then "hello " will be returned. + */ + public String getWordAtCursor(String separators) { + // getWordRangeAtCursor returns null if the connection is null + Range r = getWordRangeAtCursor(separators, 0); + return (r == null) ? null : r.mWord; + } + + private int getCursorPosition() { + mIC = mParent.getCurrentInputConnection(); + if (null == mIC) return INVALID_CURSOR_POSITION; + final ExtractedText extracted = mIC.getExtractedText(new ExtractedTextRequest(), 0); + if (extracted == null) { + return INVALID_CURSOR_POSITION; + } + return extracted.startOffset + extracted.selectionStart; + } + + /** + * Returns the text surrounding the cursor. + * + * @param sep a string of characters that split words. + * @param additionalPrecedingWordsCount the number of words before the current word that should + * be included in the returned range + * @return a range containing the text surrounding the cursor + */ + public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) { + mIC = mParent.getCurrentInputConnection(); + if (mIC == null || sep == null) { + return null; + } + CharSequence before = mIC.getTextBeforeCursor(1000, 0); + CharSequence after = mIC.getTextAfterCursor(1000, 0); + if (before == null || after == null) { + return null; + } + + // Going backward, alternate skipping non-separators and separators until enough words + // have been read. + int start = before.length(); + boolean isStoppingAtWhitespace = true; // toggles to indicate what to stop at + while (true) { // see comments below for why this is guaranteed to halt + while (start > 0) { + final int codePoint = Character.codePointBefore(before, start); + if (isStoppingAtWhitespace == isSeparator(codePoint, sep)) { + break; // inner loop + } + --start; + if (Character.isSupplementaryCodePoint(codePoint)) { + --start; + } + } + // isStoppingAtWhitespace is true every other time through the loop, + // so additionalPrecedingWordsCount is guaranteed to become < 0, which + // guarantees outer loop termination + if (isStoppingAtWhitespace && (--additionalPrecedingWordsCount < 0)) { + break; // outer loop + } + isStoppingAtWhitespace = !isStoppingAtWhitespace; + } + + // Find last word separator after the cursor + int end = -1; + while (++end < after.length()) { + final int codePoint = Character.codePointAt(after, end); + if (isSeparator(codePoint, sep)) { + break; + } + if (Character.isSupplementaryCodePoint(codePoint)) { + ++end; + } + } + + int cursor = getCursorPosition(); + if (start >= 0 && cursor + end <= after.length() + before.length()) { + String word = before.toString().substring(start, before.length()) + + after.toString().substring(0, end); + return new Range(before.length() - start, end, word); + } + + return null; + } + + public boolean isCursorTouchingWord(final SettingsValues settingsValues) { + CharSequence before = getTextBeforeCursor(1, 0); + CharSequence after = getTextAfterCursor(1, 0); + if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0)) + && !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) { + return true; + } + if (!TextUtils.isEmpty(after) && !settingsValues.isWordSeparator(after.charAt(0)) + && !settingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) { + return true; + } + return false; + } + + public void removeTrailingSpace() { + checkBatchEdit(); + final CharSequence lastOne = getTextBeforeCursor(1, 0); + if (lastOne != null && lastOne.length() == 1 + && lastOne.charAt(0) == Keyboard.CODE_SPACE) { + deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } + } + } + + public boolean sameAsTextBeforeCursor(final CharSequence text) { + final CharSequence beforeText = getTextBeforeCursor(text.length(), 0); + return TextUtils.equals(text, beforeText); + } + + /* (non-javadoc) + * Returns the word before the cursor if the cursor is at the end of a word, null otherwise + */ + public CharSequence getWordBeforeCursorIfAtEndOfWord(final SettingsValues settings) { + // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace, + // separator or end of line/text) + // Example: "test|"<EOL> "te|st" get rejected here + final CharSequence textAfterCursor = getTextAfterCursor(1, 0); + if (!TextUtils.isEmpty(textAfterCursor) + && !settings.isWordSeparator(textAfterCursor.charAt(0))) return null; + + // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe) + // Example: " -|" gets rejected here but "e-|" and "e|" are okay + CharSequence word = getWordAtCursor(settings.mWordSeparators); + // We don't suggest on leading single quotes, so we have to remove them from the word if + // it starts with single quotes. + while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) { + word = word.subSequence(1, word.length()); + } + if (TextUtils.isEmpty(word)) return null; + // Find the last code point of the string + final int lastCodePoint = Character.codePointBefore(word, word.length()); + // If for some reason the text field contains non-unicode binary data, or if the + // charsequence is exactly one char long and the contents is a low surrogate, return null. + if (!Character.isDefined(lastCodePoint)) return null; + // Bail out if the cursor is not at the end of a word (cursor must be preceded by + // non-whitespace, non-separator, non-start-of-text) + // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here. + if (settings.isWordSeparator(lastCodePoint)) return null; + final char firstChar = word.charAt(0); // we just tested that word is not empty + if (word.length() == 1 && !Character.isLetter(firstChar)) return null; + + // We only suggest on words that start with a letter or a symbol that is excluded from + // word separators (see #handleCharacterWhileInBatchEdit). + if (!(Character.isLetter(firstChar) + || settings.isSymbolExcludedFromWordSeparators(firstChar))) { + return null; + } + + return word; + } + + public boolean revertDoubleSpace() { + checkBatchEdit(); + // Here we test whether we indeed have a period and a space before us. This should not + // be needed, but it's there just in case something went wrong. + final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0); + if (!". ".equals(textBeforeCursor)) { + // Theoretically we should not be coming here if there isn't ". " before the + // cursor, but the application may be changing the text while we are typing, so + // anything goes. We should not crash. + Log.d(TAG, "Tried to revert double-space combo but we didn't find " + + "\". \" just before the cursor."); + return false; + } + deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } + commitText(" ", 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); + } + return true; + } + + public boolean revertSwapPunctuation() { + checkBatchEdit(); + // Here we test whether we indeed have a space and something else before us. This should not + // be needed, but it's there just in case something went wrong. + final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0); + // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to + // enter surrogate pairs this code will have been removed. + if (TextUtils.isEmpty(textBeforeCursor) + || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) { + // We may only come here if the application is changing the text while we are typing. + // This is quite a broken case, but not logically impossible, so we shouldn't crash, + // but some debugging log may be in order. + Log.d(TAG, "Tried to revert a swap of punctuation but we didn't " + + "find a space just before the cursor."); + return false; + } + deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } + commitText(" " + textBeforeCursor.subSequence(0, 1), 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertSwapPunctuation(); + } + return true; + } +} diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 4bb21720b..70acdc771 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -69,9 +69,7 @@ public class Settings extends InputMethodSettingsFragment public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = "pref_key_preview_popup_dismiss_delay"; public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; - public static final String PREF_BIGRAM_SUGGESTION = "next_word_suggestion"; public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; - public static final String PREF_KEY_ENABLE_SPAN_INSERT = "enable_span_insert"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; public static final String PREF_KEYPRESS_SOUND_VOLUME = @@ -87,9 +85,7 @@ public class Settings extends InputMethodSettingsFragment private ListPreference mShowCorrectionSuggestionsPreference; private ListPreference mAutoCorrectionThresholdPreference; private ListPreference mKeyPreviewPopupDismissDelay; - // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary - private CheckBoxPreference mBigramSuggestion; - // Prediction: use bigrams to predict the next word when there is no input for it yet + // Use bigrams to predict the next word when there is no input for it yet private CheckBoxPreference mBigramPrediction; private Preference mDebugSettingsPreference; @@ -100,7 +96,6 @@ public class Settings extends InputMethodSettingsFragment final String autoCorrectionOff = getResources().getString( R.string.auto_correction_threshold_mode_index_off); final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); - mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff)); if (null != mBigramPrediction) { mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); } @@ -128,7 +123,6 @@ public class Settings extends InputMethodSettingsFragment mAutoCorrectionThresholdPreference = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); - mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTION); mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS); mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); if (mDebugSettingsPreference != null) { @@ -155,9 +149,6 @@ public class Settings extends InputMethodSettingsFragment final PreferenceGroup advancedSettings = (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS); - // Remove those meaningless options for now. TODO: delete them for good - advancedSettings.removePreference(findPreference(PREF_BIGRAM_SUGGESTION)); - advancedSettings.removePreference(findPreference(PREF_KEY_ENABLE_SPAN_INSERT)); if (!VibratorUtils.getInstance(context).hasVibrator()) { generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); if (null != advancedSettings) { // Theoretically advancedSettings cannot be null @@ -165,19 +156,30 @@ public class Settings extends InputMethodSettingsFragment } } - final boolean showPopupOption = res.getBoolean( + final boolean showKeyPreviewPopupOption = res.getBoolean( R.bool.config_enable_show_popup_on_keypress_option); - if (!showPopupOption) { + mKeyPreviewPopupDismissDelay = + (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + if (!showKeyPreviewPopupOption) { generalSettings.removePreference(findPreference(PREF_POPUP_ON)); - } - - final boolean showBigramSuggestionsOption = res.getBoolean( - R.bool.config_enable_next_word_suggestions_option); - if (!showBigramSuggestionsOption) { - textCorrectionGroup.removePreference(mBigramSuggestion); - if (null != mBigramPrediction) { - textCorrectionGroup.removePreference(mBigramPrediction); + if (null != advancedSettings) { // Theoretically advancedSettings cannot be null + advancedSettings.removePreference(mKeyPreviewPopupDismissDelay); + } + } else { + final String[] entries = new String[] { + res.getString(R.string.key_preview_popup_dismiss_no_delay), + res.getString(R.string.key_preview_popup_dismiss_default_delay), + }; + final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( + R.integer.config_key_preview_linger_timeout)); + mKeyPreviewPopupDismissDelay.setEntries(entries); + mKeyPreviewPopupDismissDelay.setEntryValues( + new String[] { "0", popupDismissDelayDefaultValue }); + if (null == mKeyPreviewPopupDismissDelay.getValue()) { + mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); } + mKeyPreviewPopupDismissDelay.setEnabled( + SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); } final CheckBoxPreference includeOtherImesInLanguageSwitchList = @@ -185,23 +187,6 @@ public class Settings extends InputMethodSettingsFragment includeOtherImesInLanguageSwitchList.setEnabled( !SettingsValues.isLanguageSwitchKeySupressed(prefs)); - mKeyPreviewPopupDismissDelay = - (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - final String[] entries = new String[] { - res.getString(R.string.key_preview_popup_dismiss_no_delay), - res.getString(R.string.key_preview_popup_dismiss_default_delay), - }; - final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( - R.integer.config_key_preview_linger_timeout)); - mKeyPreviewPopupDismissDelay.setEntries(entries); - mKeyPreviewPopupDismissDelay.setEntryValues( - new String[] { "0", popupDismissDelayDefaultValue }); - if (null == mKeyPreviewPopupDismissDelay.getValue()) { - mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); - } - mKeyPreviewPopupDismissDelay.setEnabled( - SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); - final PreferenceScreen dictionaryLink = (PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY); final Intent intent = dictionaryLink.getIntent(); @@ -327,13 +312,15 @@ public class Settings extends InputMethodSettingsFragment private void updateKeyPreviewPopupDelaySummary() { final ListPreference lp = mKeyPreviewPopupDismissDelay; - lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]); + final CharSequence[] entries = lp.getEntries(); + if (entries == null || entries.length <= 0) return; + lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]); } private void updateVoiceModeSummary() { mVoicePreference.setSummary( getResources().getStringArray(R.array.voice_input_modes_summary) - [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); + [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); } private void refreshEnablingsOfKeypressSoundAndVibrationSettings( diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index b07c3e59f..10025daf8 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.content.res.Resources; import android.util.Log; import android.view.inputmethod.EditorInfo; @@ -29,7 +30,6 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Map; /** * When you call the constructor of this class, you may want to change the current system locale by @@ -38,6 +38,19 @@ import java.util.Map; public class SettingsValues { private static final String TAG = SettingsValues.class.getSimpleName(); + private static final int SUGGESTION_VISIBILITY_SHOW_VALUE + = R.string.prefs_suggestion_visibility_show_value; + private static final int SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE + = R.string.prefs_suggestion_visibility_show_only_portrait_value; + private static final int SUGGESTION_VISIBILITY_HIDE_VALUE + = R.string.prefs_suggestion_visibility_hide_value; + + private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] { + SUGGESTION_VISIBILITY_SHOW_VALUE, + SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE, + SUGGESTION_VISIBILITY_HIDE_VALUE + }; + // From resources: public final int mDelayUpdateOldSuggestions; public final String mWeakSpaceStrippers; @@ -63,27 +76,30 @@ public class SettingsValues { @SuppressWarnings("unused") // TODO: Use this private final String mKeyPreviewPopupDismissDelayRawValue; public final boolean mUseContactsDict; - // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary - public final boolean mBigramSuggestionEnabled; - // Prediction: use bigrams to predict the next word when there is no input for it yet + // Use bigrams to predict the next word when there is no input for it yet public final boolean mBigramPredictionEnabled; - public final boolean mEnableSuggestionSpanInsertion; @SuppressWarnings("unused") // TODO: Use this private final int mVibrationDurationSettingsRawValue; @SuppressWarnings("unused") // TODO: Use this private final float mKeypressSoundVolumeRawValue; private final InputMethodSubtype[] mAdditionalSubtypes; + // From the input box + private final InputAttributes mInputAttributes; + // Deduced settings public final int mKeypressVibrationDuration; public final float mFxVolume; public final int mKeyPreviewPopupDismissDelay; - public final boolean mAutoCorrectEnabled; + private final boolean mAutoCorrectEnabled; public final float mAutoCorrectionThreshold; + public final boolean mCorrectionEnabled; + public final int mSuggestionVisibility; private final boolean mVoiceKeyEnabled; private final boolean mVoiceKeyOnMain; - public SettingsValues(final SharedPreferences prefs, final Context context) { + public SettingsValues(final SharedPreferences prefs, final InputAttributes inputAttributes, + final Context context) { final Resources res = context.getResources(); // Get the resources @@ -109,6 +125,13 @@ public class SettingsValues { mSymbolsExcludedFromWordSeparators, res); mHintToSaveText = context.getText(R.string.hint_add_to_dictionary); + // Store the input attributes + if (null == inputAttributes) { + mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); + } else { + mInputAttributes = inputAttributes; + } + // Get the settings preferences mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); mVibrateOn = isVibrateOn(context, prefs, res); @@ -131,12 +154,7 @@ public class SettingsValues { Integer.toString(res.getInteger(R.integer.config_key_preview_linger_timeout))); mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue); - mBigramSuggestionEnabled = mAutoCorrectEnabled - && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled); - mBigramPredictionEnabled = mBigramSuggestionEnabled - && isBigramPredictionEnabled(prefs, res); - // TODO: remove mEnableSuggestionSpanInsertion. It's always true. - mEnableSuggestionSpanInsertion = true; + mBigramPredictionEnabled = isBigramPredictionEnabled(prefs, res); mVibrationDurationSettingsRawValue = prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1); mKeypressSoundVolumeRawValue = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f); @@ -151,22 +169,23 @@ public class SettingsValues { mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray( getPrefAdditionalSubtypes(prefs, res)); + mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; + mSuggestionVisibility = createSuggestionVisibility(res); } // Helper functions to create member values. private static SuggestedWords createSuggestPuncList(final String[] puncs) { - final ArrayList<SuggestedWords.SuggestedWordInfo> puncList = - new ArrayList<SuggestedWords.SuggestedWordInfo>(); + final ArrayList<SuggestedWordInfo> puncList = new ArrayList<SuggestedWordInfo>(); if (puncs != null) { for (final String puncSpec : puncs) { - puncList.add(new SuggestedWords.SuggestedWordInfo( - KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE)); + puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec), + SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED, + Dictionary.TYPE_HARDCODED)); } } return new SuggestedWords(puncList, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, - false /* allowsToBeAutoCorrected */, true /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, false /* isPrediction */); @@ -184,6 +203,16 @@ public class SettingsValues { return wordSeparators; } + private int createSuggestionVisibility(final Resources res) { + final String suggestionVisiblityStr = mShowSuggestionsSetting; + for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) { + if (suggestionVisiblityStr.equals(res.getString(visibility))) { + return visibility; + } + } + throw new RuntimeException("Bug: visibility string is not configured correctly"); + } + private static boolean isVibrateOn(final Context context, final SharedPreferences prefs, final Resources res) { final boolean hasVibrator = VibratorUtils.getInstance(context).hasVibrator(); @@ -191,6 +220,22 @@ public class SettingsValues { res.getBoolean(R.bool.config_default_vibration_enabled)); } + public boolean isApplicationSpecifiedCompletionsOn() { + return mInputAttributes.mApplicationSpecifiedCompletionOn; + } + + public boolean isSuggestionsRequested(final int displayOrientation) { + return mInputAttributes.mIsSettingsSuggestionStripOn + && (mCorrectionEnabled + || isSuggestionStripVisibleInOrientation(displayOrientation)); + } + + public boolean isSuggestionStripVisibleInOrientation(final int orientation) { + return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE) + || (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE + && orientation == Configuration.ORIENTATION_PORTRAIT); + } + public boolean isWordSeparator(int code) { return mWordSeparators.contains(String.valueOf((char)code)); } @@ -240,12 +285,6 @@ public class SettingsValues { R.integer.config_key_preview_linger_timeout)))); } - private static boolean isBigramSuggestionEnabled(final SharedPreferences sp, - final Resources resources, final boolean autoCorrectEnabled) { - // TODO: remove this method. Bigram suggestion is always true. - return true; - } - private static boolean isBigramPredictionEnabled(final SharedPreferences sp, final Resources resources) { return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean( @@ -367,4 +406,9 @@ public class SettingsValues { final String newStr = Utils.localeAndTimeHashMapToStr(map); prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply(); } + + // For debug. + public String getInputAttributesDebugString() { + return mInputAttributes.toString(); + } } diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index a43b90525..6e7d985d6 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -184,6 +184,9 @@ public class StringUtils { final char[] characters = string.toCharArray(); final int length = characters.length; final int[] codePoints = new int[Character.codePointCount(characters, 0, length)]; + if (length <= 0) { + return new int[0]; + } int codePoint = Character.codePointAt(characters, 0); int dsti = 0; for (int srci = Character.charCount(codePoint); diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 336a76f4b..31c6000e3 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin; import android.content.Context; import android.text.TextUtils; -import android.util.Log; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; @@ -26,6 +25,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.io.File; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; @@ -34,79 +34,47 @@ import java.util.concurrent.ConcurrentHashMap; * This class loads a dictionary and provides a list of suggestions for a given sequence of * characters. This includes corrections and completions. */ -public class Suggest implements Dictionary.WordCallback { +public class Suggest { public static final String TAG = Suggest.class.getSimpleName(); - public static final int APPROX_MAX_WORD_LENGTH = 32; - + // TODO: rename this to CORRECTION_OFF public static final int CORRECTION_NONE = 0; + // TODO: rename this to CORRECTION_ON public static final int CORRECTION_FULL = 1; - public static final int CORRECTION_FULL_BIGRAM = 2; - - // It seems the following values are only used for logging. - public static final int DIC_USER_TYPED = 0; - public static final int DIC_MAIN = 1; - public static final int DIC_USER = 2; - public static final int DIC_USER_HISTORY = 3; - public static final int DIC_CONTACTS = 4; - public static final int DIC_WHITELIST = 6; - // If you add a type of dictionary, increment DIC_TYPE_LAST_ID - // TODO: this value seems unused. Remove it? - public static final int DIC_TYPE_LAST_ID = 6; - public static final String DICT_KEY_MAIN = "main"; - public static final String DICT_KEY_CONTACTS = "contacts"; - // User dictionary, the system-managed one. - public static final String DICT_KEY_USER = "user"; - // User history dictionary for the unigram map, internal to LatinIME - public static final String DICT_KEY_USER_HISTORY_UNIGRAM = "history_unigram"; - // User history dictionary for the bigram map, internal to LatinIME - public static final String DICT_KEY_USER_HISTORY_BIGRAM = "history_bigram"; - public static final String DICT_KEY_WHITELIST ="whitelist"; private static final boolean DBG = LatinImeLogger.sDBG; - private boolean mHasMainDictionary; - private Dictionary mContactsDict; + private Dictionary mMainDictionary; + private ContactsBinaryDictionary mContactsDict; private WhitelistDictionary mWhiteListDictionary; - private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries = - new ConcurrentHashMap<String, Dictionary>(); - private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries = + private final ConcurrentHashMap<String, Dictionary> mDictionaries = new ConcurrentHashMap<String, Dictionary>(); - private int mPrefMaxSuggestions = 18; - - private static final int PREF_MAX_BIGRAMS = 60; + public static final int MAX_SUGGESTIONS = 18; private float mAutoCorrectionThreshold; - private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>(); - private ArrayList<SuggestedWordInfo> mBigramSuggestions = new ArrayList<SuggestedWordInfo>(); - private CharSequence mConsideredWord; - - // TODO: Remove these member variables by passing more context to addWord() callback method - private boolean mIsFirstCharCapitalized; - private boolean mIsAllUpperCase; - private int mTrailingSingleQuotesCount; - - private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; + // Locale used for upper- and title-casing words + final private Locale mLocale; public Suggest(final Context context, final Locale locale) { initAsynchronously(context, locale); + mLocale = locale; } /* package for test */ Suggest(final Context context, final File dictionary, final long startOffset, final long length, final Locale locale) { final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset, length /* useFullEditDistance */, false, locale); - mHasMainDictionary = null != mainDict; - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict); + mLocale = locale; + mMainDictionary = mainDict; + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); initWhitelistAndAutocorrectAndPool(context, locale); } private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) { mWhiteListDictionary = new WhitelistDictionary(context, locale); - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary); + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary); } private void initAsynchronously(final Context context, final Locale locale) { @@ -129,15 +97,14 @@ public class Suggest implements Dictionary.WordCallback { } public void resetMainDict(final Context context, final Locale locale) { - mHasMainDictionary = false; + mMainDictionary = null; new Thread("InitializeBinaryDictionary") { @Override public void run() { final DictionaryCollection newMainDict = DictionaryFactory.createMainDictionaryFromManager(context, locale); - mHasMainDictionary = null != newMainDict && !newMainDict.isEmpty(); - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict); + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict); + mMainDictionary = newMainDict; } }.start(); } @@ -145,27 +112,27 @@ public class Suggest implements Dictionary.WordCallback { // The main dictionary could have been loaded asynchronously. Don't cache the return value // of this method. public boolean hasMainDictionary() { - return mHasMainDictionary; + return null != mMainDictionary && mMainDictionary.isInitialized(); } - public Dictionary getContactsDictionary() { - return mContactsDict; + public Dictionary getMainDictionary() { + return mMainDictionary; } - public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { - return mUnigramDictionaries; + public ContactsBinaryDictionary getContactsDictionary() { + return mContactsDict; } - public static int getApproxMaxWordLength() { - return APPROX_MAX_WORD_LENGTH; + public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { + return mDictionaries; } /** * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted * before the main dictionary, if set. This refers to the system-managed user dictionary. */ - public void setUserDictionary(Dictionary userDictionary) { - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary); + public void setUserDictionary(UserBinaryDictionary userDictionary) { + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary); } /** @@ -173,236 +140,191 @@ public class Suggest implements Dictionary.WordCallback { * the contacts dictionary by passing null to this method. In this case no contacts dictionary * won't be used. */ - public void setContactsDictionary(Dictionary contactsDictionary) { + public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) { mContactsDict = contactsDictionary; - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary); } - public void setUserHistoryDictionary(Dictionary userHistoryDictionary) { - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM, - userHistoryDictionary); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM, - userHistoryDictionary); + public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) { + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); } public void setAutoCorrectionThreshold(float threshold) { mAutoCorrectionThreshold = threshold; } - private static CharSequence capitalizeWord(final boolean all, final boolean first, - final CharSequence word) { - if (TextUtils.isEmpty(word) || !(all || first)) return word; - final int wordLength = word.length(); - final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); - // TODO: Must pay attention to locale when changing case. - if (all) { - sb.append(word.toString().toUpperCase()); - } else if (first) { - sb.append(Character.toUpperCase(word.charAt(0))); - if (wordLength > 1) { - sb.append(word.subSequence(1, wordLength)); - } - } - return sb; - } - - protected void addBigramToSuggestions(SuggestedWordInfo bigram) { - mSuggestions.add(bigram); - } - - private static final WordComposer sEmptyWordComposer = new WordComposer(); - public SuggestedWords getBigramPredictions(CharSequence prevWordForBigram) { + public SuggestedWords getSuggestedWords( + final WordComposer wordComposer, CharSequence prevWordForBigram, + final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) { LatinImeLogger.onStartSuggestion(prevWordForBigram); - mIsFirstCharCapitalized = false; - mIsAllUpperCase = false; - mTrailingSingleQuotesCount = 0; - mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions); - - // Treating USER_TYPED as UNIGRAM suggestion for logging now. - LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); - mConsideredWord = ""; - - mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS); - - getAllBigrams(prevWordForBigram, sEmptyWordComposer); - - // Nothing entered: return all bigrams for the previous word - int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); - for (int i = 0; i < insertCount; ++i) { - addBigramToSuggestions(mBigramSuggestions.get(i)); + if (wordComposer.isBatchMode()) { + return getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo); + } else { + return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, + isCorrectionEnabled); } - - SuggestedWordInfo.removeDups(mSuggestions); - - return new SuggestedWords(mSuggestions, - false /* typedWordValid */, - false /* hasAutoCorrectionCandidate */, - false /* allowsToBeAutoCorrected */, - false /* isPunctuationSuggestions */, - false /* isObsoleteSuggestions */, - true /* isPrediction */); } - // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder - public SuggestedWords getSuggestedWords( + // Retrieves suggestions for the typing input. + private SuggestedWords getSuggestedWordsForTypingInput( final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo, final int correctionMode) { - LatinImeLogger.onStartSuggestion(prevWordForBigram); - mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); - mIsAllUpperCase = wordComposer.isAllUpperCase(); - mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); - mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions); + final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) { + final boolean isPrediction = !wordComposer.isComposingWord(); + final boolean isFirstCharCapitalized = + !isPrediction && wordComposer.isFirstCharCapitalized(); + final boolean isAllUpperCase = !isPrediction && wordComposer.isAllUpperCase(); + final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); + final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, + MAX_SUGGESTIONS); final String typedWord = wordComposer.getTypedWord(); - final String consideredWord = mTrailingSingleQuotesCount > 0 - ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount) + final String consideredWord = trailingSingleQuotesCount > 0 + ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) : typedWord; - // Treating USER_TYPED as UNIGRAM suggestion for logging now. - LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); - mConsideredWord = consideredWord; + LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED); - if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) { + final WordComposer wordComposerForLookup; + if (trailingSingleQuotesCount > 0) { + wordComposerForLookup = new WordComposer(wordComposer); + for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { + wordComposerForLookup.deleteLast(); + } + } else { + wordComposerForLookup = wordComposer; + } + if (wordComposerForLookup.size() <= 1) { // At first character typed, search only the bigrams - mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS); - if (!TextUtils.isEmpty(prevWordForBigram)) { - getAllBigrams(prevWordForBigram, wordComposer); - if (TextUtils.isEmpty(consideredWord)) { - // Nothing entered: return all bigrams for the previous word - int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); - for (int i = 0; i < insertCount; ++i) { - addBigramToSuggestions(mBigramSuggestions.get(i)); - } - } else { - // Word entered: return only bigrams that match the first char of the typed word - final char currentChar = consideredWord.charAt(0); - // TODO: Must pay attention to locale when changing case. - // TODO: Use codepoint instead of char - final char currentCharUpper = Character.toUpperCase(currentChar); - int count = 0; - final int bigramSuggestionSize = mBigramSuggestions.size(); - for (int i = 0; i < bigramSuggestionSize; i++) { - final SuggestedWordInfo bigramSuggestion = mBigramSuggestions.get(i); - final char bigramSuggestionFirstChar = - (char)bigramSuggestion.codePointAt(0); - if (bigramSuggestionFirstChar == currentChar - || bigramSuggestionFirstChar == currentCharUpper) { - addBigramToSuggestions(bigramSuggestion); - if (++count > mPrefMaxSuggestions) break; - } - } - } - } - - } else if (wordComposer.size() > 1) { - final WordComposer wordComposerForLookup; - if (mTrailingSingleQuotesCount > 0) { - wordComposerForLookup = new WordComposer(wordComposer); - for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { - wordComposerForLookup.deleteLast(); + for (final String key : mDictionaries.keySet()) { + final Dictionary dictionary = mDictionaries.get(key); + suggestionsSet.addAll(dictionary.getSuggestions(wordComposerForLookup, + prevWordForBigram, proximityInfo)); } - } else { - wordComposerForLookup = wordComposer; } + } else { // At second character typed, search the unigrams (scores being affected by bigrams) - for (final String key : mUnigramDictionaries.keySet()) { - // Skip UserUnigramDictionary and WhitelistDictionary to lookup - if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) - continue; - final Dictionary dictionary = mUnigramDictionaries.get(key); - dictionary.getWords(wordComposerForLookup, prevWordForBigram, this, proximityInfo); + for (final String key : mDictionaries.keySet()) { + final Dictionary dictionary = mDictionaries.get(key); + suggestionsSet.addAll(dictionary.getSuggestions( + wordComposerForLookup, prevWordForBigram, proximityInfo)); } } - final CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, - mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord)); + // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" + // but still autocorrected from - in the case the whitelist only capitalizes the word. + // The whitelist should be case-insensitive, so it's not possible to be consistent with + // a boolean flag. Right now this is handled with a slight hack in + // WhitelistDictionary#shouldForciblyAutoCorrectFrom. + final boolean allowsToBeAutoCorrected = AutoCorrection.isWhitelistedOrNotAWord( + mDictionaries, consideredWord, wordComposer.isFirstCharCapitalized()); + + final CharSequence whitelistedWord = + mWhiteListDictionary.getWhitelistedWord(consideredWord); final boolean hasAutoCorrection; - if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) { - final CharSequence autoCorrection = - AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer, - mSuggestions, consideredWord, mAutoCorrectionThreshold, - whitelistedWord); - hasAutoCorrection = (null != autoCorrection); + // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because + // any attempt to do auto-correction is already shielded with a test for this flag; at the + // same time, it feels wrong that the SuggestedWord object includes information about + // the current settings. It may also be useful to know, when the setting is off, whether + // the word *would* have been auto-corrected. + if (!isCorrectionEnabled || !allowsToBeAutoCorrected || wordComposer.isMostlyCaps() + || wordComposer.isResumed() || !hasMainDictionary()) { + // If we don't have a main dictionary, we never want to auto-correct. The reason for + // this is, the user may have a contact whose name happens to match a valid word in + // their language, and it will unexpectedly auto-correct. For example, if the user + // types in English with no dictionary and has a "Will" in their contact list, "will" + // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no + // auto-correct. + hasAutoCorrection = false; + } else if (null != whitelistedWord) { + hasAutoCorrection = true; + } else if (suggestionsSet.isEmpty()) { + hasAutoCorrection = false; + } else if (AutoCorrection.suggestionExceedsAutoCorrectionThreshold(suggestionsSet.first(), + consideredWord, mAutoCorrectionThreshold)) { + hasAutoCorrection = true; } else { hasAutoCorrection = false; } if (whitelistedWord != null) { - if (mTrailingSingleQuotesCount > 0) { - final StringBuilder sb = new StringBuilder(whitelistedWord); - for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { - sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); - } - mSuggestions.add(0, new SuggestedWordInfo( - sb.toString(), SuggestedWordInfo.MAX_SCORE)); - } else { - mSuggestions.add(0, new SuggestedWordInfo( - whitelistedWord, SuggestedWordInfo.MAX_SCORE)); + suggestionsSet.add(new SuggestedWordInfo(whitelistedWord, + SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST, + Dictionary.TYPE_WHITELIST)); + } + + final ArrayList<SuggestedWordInfo> suggestionsContainer = + new ArrayList<SuggestedWordInfo>(suggestionsSet); + final int suggestionsCount = suggestionsContainer.size(); + if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) { + for (int i = 0; i < suggestionsCount; ++i) { + final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( + wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, + trailingSingleQuotesCount); + suggestionsContainer.set(i, transformedWordInfo); } } - mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE)); - SuggestedWordInfo.removeDups(mSuggestions); + for (int i = 0; i < suggestionsCount; ++i) { + final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict); + } + + if (!TextUtils.isEmpty(typedWord)) { + suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, + SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, + Dictionary.TYPE_USER_TYPED)); + } + SuggestedWordInfo.removeDups(suggestionsContainer); final ArrayList<SuggestedWordInfo> suggestionsList; - if (DBG) { - suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions); + if (DBG && !suggestionsContainer.isEmpty()) { + suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer); } else { - suggestionsList = mSuggestions; + suggestionsList = suggestionsContainer; } - // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" - // but still autocorrected from - in the case the whitelist only capitalizes the word. - // The whitelist should be case-insensitive, so it's not possible to be consistent with - // a boolean flag. Right now this is handled with a slight hack in - // WhitelistDictionary#shouldForciblyAutoCorrectFrom. - final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected( - getUnigramDictionaries(), consideredWord, wordComposer.isFirstCharCapitalized()) - // If we don't have a main dictionary, we never want to auto-correct. The reason for this - // is, the user may have a contact whose name happens to match a valid word in their - // language, and it will unexpectedly auto-correct. For example, if the user types in - // English with no dictionary and has a "Will" in their contact list, "will" would - // always auto-correct to "Will" which is unwanted. Hence, no main dict => no auto-correct. - && mHasMainDictionary; - - boolean autoCorrectionAvailable = hasAutoCorrection; - if (correctionMode == CORRECTION_FULL || correctionMode == CORRECTION_FULL_BIGRAM) { - autoCorrectionAvailable |= !allowsToBeAutoCorrected; - } - // Don't auto-correct words with multiple capital letter - autoCorrectionAvailable &= !wordComposer.isMostlyCaps(); - autoCorrectionAvailable &= !wordComposer.isResumed(); - if (allowsToBeAutoCorrected && suggestionsList.size() > 1 && mAutoCorrectionThreshold > 0 - && Suggest.shouldBlockAutoCorrectionBySafetyNet(typedWord, - suggestionsList.get(1).mWord)) { - autoCorrectionAvailable = false; - } return new SuggestedWords(suggestionsList, - !allowsToBeAutoCorrected /* typedWordValid */, - autoCorrectionAvailable /* hasAutoCorrectionCandidate */, - allowsToBeAutoCorrected /* allowsToBeAutoCorrected */, + // TODO: this first argument is lying. If this is a whitelisted word which is an + // actual word, it says typedWordValid = false, which looks wrong. We should either + // rename the attribute or change the value. + !isPrediction && !allowsToBeAutoCorrected /* typedWordValid */, + !isPrediction && hasAutoCorrection, /* willAutoCorrect */ false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, - false /* isPrediction */); + isPrediction); } - /** - * Adds all bigram predictions for prevWord. Also checks the lower case version of prevWord if - * it contains any upper case characters. - */ - private void getAllBigrams(final CharSequence prevWord, final WordComposer wordComposer) { - if (StringUtils.hasUpperCase(prevWord)) { - // TODO: Must pay attention to locale when changing case. - final CharSequence lowerPrevWord = prevWord.toString().toLowerCase(); - for (final Dictionary dictionary : mBigramDictionaries.values()) { - dictionary.getBigrams(wordComposer, lowerPrevWord, this); + // Retrieves suggestions for the batch input. + private SuggestedWords getSuggestedWordsForBatchInput( + final WordComposer wordComposer, CharSequence prevWordForBigram, + final ProximityInfo proximityInfo) { + final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, + MAX_SUGGESTIONS); + + // At second character typed, search the unigrams (scores being affected by bigrams) + for (final String key : mDictionaries.keySet()) { + // Skip UserUnigramDictionary and WhitelistDictionary to lookup + if (key.equals(Dictionary.TYPE_USER_HISTORY) + || key.equals(Dictionary.TYPE_WHITELIST)) { + continue; } + final Dictionary dictionary = mDictionaries.get(key); + suggestionsSet.addAll(dictionary.getSuggestions( + wordComposer, prevWordForBigram, proximityInfo)); } - for (final Dictionary dictionary : mBigramDictionaries.values()) { - dictionary.getBigrams(wordComposer, prevWord, this); - } + + final ArrayList<SuggestedWordInfo> suggestionsContainer = + new ArrayList<SuggestedWordInfo>(suggestionsSet); + + SuggestedWordInfo.removeDups(suggestionsContainer); + return new SuggestedWords(suggestionsContainer, + true /* typedWordValid */, + true /* willAutoCorrect */, + false /* isPunctuationSuggestions */, + false /* isObsoleteSuggestions */, + false /* isPrediction */); } private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( @@ -431,119 +353,44 @@ public class Suggest implements Dictionary.WordCallback { return suggestionsList; } - // TODO: Use codepoint instead of char - @Override - public boolean addWord(final char[] word, final int offset, final int length, int score, - final int dicTypeId, final int dataType) { - int dataTypeForLog = dataType; - final ArrayList<SuggestedWordInfo> suggestions; - final int prefMaxSuggestions; - if (dataType == Dictionary.BIGRAM) { - suggestions = mBigramSuggestions; - prefMaxSuggestions = PREF_MAX_BIGRAMS; - } else { - suggestions = mSuggestions; - prefMaxSuggestions = mPrefMaxSuggestions; + private static class SuggestedWordInfoComparator implements Comparator<SuggestedWordInfo> { + // This comparator ranks the word info with the higher frequency first. That's because + // that's the order we want our elements in. + @Override + public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) { + if (o1.mScore > o2.mScore) return -1; + if (o1.mScore < o2.mScore) return 1; + if (o1.mCodePointCount < o2.mCodePointCount) return -1; + if (o1.mCodePointCount > o2.mCodePointCount) return 1; + return o1.mWord.toString().compareTo(o2.mWord.toString()); } - - int pos = 0; - - // Check if it's the same word, only caps are different - if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) { - // TODO: remove this surrounding if clause and move this logic to - // getSuggestedWordBuilder. - if (suggestions.size() > 0) { - final SuggestedWordInfo currentHighestWord = suggestions.get(0); - // If the current highest word is also equal to typed word, we need to compare - // frequency to determine the insertion position. This does not ensure strictly - // correct ordering, but ensures the top score is on top which is enough for - // removing duplicates correctly. - if (StringUtils.equalsIgnoreCase(currentHighestWord.mWord, word, offset, length) - && score <= currentHighestWord.mScore) { - pos = 1; - } - } - } else { - // Check the last one's score and bail - if (suggestions.size() >= prefMaxSuggestions - && suggestions.get(prefMaxSuggestions - 1).mScore >= score) return true; - while (pos < suggestions.size()) { - final int curScore = suggestions.get(pos).mScore; - if (curScore < score - || (curScore == score && length < suggestions.get(pos).codePointCount())) { - break; - } - pos++; - } - } - if (pos >= prefMaxSuggestions) { - return true; - } - - final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); - // TODO: Must pay attention to locale when changing case. - if (mIsAllUpperCase) { - sb.append(new String(word, offset, length).toUpperCase()); - } else if (mIsFirstCharCapitalized) { - sb.append(Character.toUpperCase(word[offset])); - if (length > 1) { - sb.append(word, offset + 1, length - 1); - } + } + private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator = + new SuggestedWordInfoComparator(); + + private static SuggestedWordInfo getTransformedSuggestedWordInfo( + final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, + final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { + final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); + if (isAllUpperCase) { + sb.append(wordInfo.mWord.toString().toUpperCase(locale)); + } else if (isFirstCharCapitalized) { + sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale)); } else { - sb.append(word, offset, length); + sb.append(wordInfo.mWord); } - for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { + for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); } - suggestions.add(pos, new SuggestedWordInfo(sb, score)); - if (suggestions.size() > prefMaxSuggestions) { - suggestions.remove(prefMaxSuggestions); - } else { - LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog); - } - return true; + return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict); } public void close() { final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>(); - dictionaries.addAll(mUnigramDictionaries.values()); - dictionaries.addAll(mBigramDictionaries.values()); + dictionaries.addAll(mDictionaries.values()); for (final Dictionary dictionary : dictionaries) { dictionary.close(); } - mHasMainDictionary = false; - } - - // TODO: Resolve the inconsistencies between the native auto correction algorithms and - // this safety net - public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord, - final CharSequence suggestion) { - // Safety net for auto correction. - // Actually if we hit this safety net, it's a bug. - // If user selected aggressive auto correction mode, there is no need to use the safety - // net. - // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH, - // we should not use net because relatively edit distance can be big. - final int typedWordLength = typedWord.length(); - if (typedWordLength < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) { - return false; - } - final int maxEditDistanceOfNativeDictionary = - (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1; - final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString()); - if (DBG) { - Log.d(TAG, "Autocorrected edit distance = " + distance - + ", " + maxEditDistanceOfNativeDictionary); - } - if (distance > maxEditDistanceOfNativeDictionary) { - if (DBG) { - Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion); - Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. " - + "Turning off auto-correction."); - } - return true; - } else { - return false; - } + mMainDictionary = null; } } diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 497fd3bfa..f079c2112 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -25,27 +25,27 @@ import java.util.HashSet; public class SuggestedWords { public static final SuggestedWords EMPTY = new SuggestedWords( - new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false, false); + new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false); public final boolean mTypedWordValid; - public final boolean mHasAutoCorrectionCandidate; + // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition + // of what this flag means would be "the top suggestion is strong enough to auto-correct", + // whether this exactly matches the user entry or not. + public final boolean mWillAutoCorrect; public final boolean mIsPunctuationSuggestions; - public final boolean mAllowsToBeAutoCorrected; public final boolean mIsObsoleteSuggestions; public final boolean mIsPrediction; private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, final boolean typedWordValid, - final boolean hasAutoCorrectionCandidate, - final boolean allowsToBeAutoCorrected, + final boolean willAutoCorrect, final boolean isPunctuationSuggestions, final boolean isObsoleteSuggestions, final boolean isPrediction) { mSuggestedWordInfoList = suggestedWordInfoList; mTypedWordValid = typedWordValid; - mHasAutoCorrectionCandidate = hasAutoCorrectionCandidate; - mAllowsToBeAutoCorrected = allowsToBeAutoCorrected; + mWillAutoCorrect = willAutoCorrect; mIsPunctuationSuggestions = isPunctuationSuggestions; mIsObsoleteSuggestions = isObsoleteSuggestions; mIsPrediction = isPrediction; @@ -55,7 +55,7 @@ public class SuggestedWords { return mSuggestedWordInfoList.size(); } - public CharSequence getWord(int pos) { + public String getWord(int pos) { return mSuggestedWordInfoList.get(pos).mWord; } @@ -67,12 +67,8 @@ public class SuggestedWords { return mSuggestedWordInfoList.get(pos); } - public boolean hasAutoCorrectionWord() { - return mHasAutoCorrectionCandidate && size() > 1 && !mTypedWordValid; - } - public boolean willAutoCorrect() { - return !mTypedWordValid && mHasAutoCorrectionCandidate; + return mWillAutoCorrect; } @Override @@ -80,8 +76,7 @@ public class SuggestedWords { // Pretty-print method to help debug return "SuggestedWords:" + " mTypedWordValid=" + mTypedWordValid - + " mHasAutoCorrectionCandidate=" + mHasAutoCorrectionCandidate - + " mAllowsToBeAutoCorrected=" + mAllowsToBeAutoCorrected + + " mWillAutoCorrect=" + mWillAutoCorrect + " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray()); } @@ -91,7 +86,8 @@ public class SuggestedWords { final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>(); for (CompletionInfo info : infos) { if (null != info && info.getText() != null) { - result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE)); + result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE, + SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED)); } } return result; @@ -103,7 +99,8 @@ public class SuggestedWords { final CharSequence typedWord, final SuggestedWords previousSuggestions) { final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>(); final HashSet<String> alreadySeen = new HashSet<String>(); - suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE)); + suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, + SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED)); alreadySeen.add(typedWord.toString()); final int previousSize = previousSuggestions.size(); for (int pos = 1; pos < previousSize; pos++) { @@ -120,17 +117,28 @@ public class SuggestedWords { public static class SuggestedWordInfo { public static final int MAX_SCORE = Integer.MAX_VALUE; - private final String mWordStr; - public final CharSequence mWord; + public static final int KIND_TYPED = 0; // What user typed + public static final int KIND_CORRECTION = 1; // Simple correction/suggestion + public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars) + public static final int KIND_WHITELIST = 3; // Whitelisted word + public static final int KIND_BLACKLIST = 4; // Blacklisted word + public static final int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation + public static final int KIND_APP_DEFINED = 6; // Suggested by the application + public static final int KIND_SHORTCUT = 7; // A shortcut + public final String mWord; public final int mScore; + public final int mKind; // one of the KIND_* constants above public final int mCodePointCount; + public final String mSourceDict; private String mDebugString = ""; - public SuggestedWordInfo(final CharSequence word, final int score) { - mWordStr = word.toString(); - mWord = word; + public SuggestedWordInfo(final CharSequence word, final int score, final int kind, + final String sourceDict) { + mWord = word.toString(); mScore = score; - mCodePointCount = mWordStr.codePointCount(0, mWordStr.length()); + mKind = kind; + mSourceDict = sourceDict; + mCodePointCount = StringUtils.codePointCount(mWord); } @@ -148,15 +156,15 @@ public class SuggestedWords { } public int codePointAt(int i) { - return mWordStr.codePointAt(i); + return mWord.codePointAt(i); } @Override public String toString() { if (TextUtils.isEmpty(mDebugString)) { - return mWordStr; + return mWord; } else { - return mWordStr + " (" + mDebugString.toString() + ")"; + return mWord + " (" + mDebugString.toString() + ")"; } } @@ -170,7 +178,7 @@ public class SuggestedWords { final SuggestedWordInfo cur = candidates.get(i); for (int j = 0; j < i; ++j) { final SuggestedWordInfo previous = candidates.get(j); - if (TextUtils.equals(cur.mWord, previous.mWord)) { + if (cur.mWord.equals(previous.mWord)) { candidates.remove(cur.mScore < previous.mScore ? i : j); --i; break; diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java index 673b54500..bdd988df2 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java @@ -19,22 +19,23 @@ package com.android.inputmethod.latin; import android.content.Context; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import java.util.ArrayList; import java.util.Locale; public class SynchronouslyLoadedContactsBinaryDictionary extends ContactsBinaryDictionary { private boolean mClosed; public SynchronouslyLoadedContactsBinaryDictionary(final Context context, final Locale locale) { - super(context, Suggest.DIC_CONTACTS, locale); + super(context, locale); } @Override - public synchronized void getWords(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { + public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, + final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { syncReloadDictionaryIfRequired(); - getWordsInner(codes, prevWordForBigrams, callback, proximityInfo); + return super.getSuggestions(codes, prevWordForBigrams, proximityInfo); } @Override diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java deleted file mode 100644 index a8b871cdf..000000000 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.content.Context; - -import com.android.inputmethod.keyboard.ProximityInfo; - -public class SynchronouslyLoadedContactsDictionary extends ContactsDictionary { - private boolean mClosed; - - public SynchronouslyLoadedContactsDictionary(final Context context) { - super(context, Suggest.DIC_CONTACTS); - mClosed = false; - } - - @Override - public synchronized void getWords(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { - blockingReloadDictionaryIfRequired(); - getWordsInner(codes, prevWordForBigrams, callback, proximityInfo); - } - - @Override - public synchronized boolean isValidWord(CharSequence word) { - blockingReloadDictionaryIfRequired(); - return getWordFrequency(word) > -1; - } - - // Protect against multiple closing - @Override - public synchronized void close() { - // Actually with the current implementation of ContactsDictionary it's safe to close - // several times, so the following protection is really only for foolproofing - if (mClosed) return; - mClosed = true; - super.close(); - } -} diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java index 1606a34e0..b8cfddd4e 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java @@ -19,6 +19,9 @@ package com.android.inputmethod.latin; import android.content.Context; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + +import java.util.ArrayList; public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary { @@ -32,11 +35,10 @@ public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionar } @Override - public synchronized void getWords(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { + public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, + final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { syncReloadDictionaryIfRequired(); - getWordsInner(codes, prevWordForBigrams, callback, proximityInfo); + return super.getSuggestions(codes, prevWordForBigrams, proximityInfo); } @Override diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java deleted file mode 100644 index 23a49c192..000000000 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.content.Context; - -import com.android.inputmethod.keyboard.ProximityInfo; - -public class SynchronouslyLoadedUserDictionary extends UserDictionary { - private boolean mClosed; - - public SynchronouslyLoadedUserDictionary(final Context context, final String locale) { - this(context, locale, false); - } - - public SynchronouslyLoadedUserDictionary(final Context context, final String locale, - final boolean alsoUseMoreRestrictiveLocales) { - super(context, locale, alsoUseMoreRestrictiveLocales); - } - - @Override - public synchronized void getWords(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { - blockingReloadDictionaryIfRequired(); - getWordsInner(codes, prevWordForBigrams, callback, proximityInfo); - } - - @Override - public synchronized boolean isValidWord(CharSequence word) { - blockingReloadDictionaryIfRequired(); - return super.isValidWord(word); - } - - // Protect against multiple closing - @Override - public synchronized void close() { - if (mClosed) return; - mClosed = true; - super.close(); - } -} diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 6fa1a25a1..60e6fa127 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -34,13 +34,27 @@ import java.util.Arrays; */ public class UserBinaryDictionary extends ExpandableBinaryDictionary { - // TODO: use Words.SHORTCUT when it's public in the SDK + // The user dictionary provider uses an empty string to mean "all languages". + private static final String USER_DICTIONARY_ALL_LANGUAGES = ""; + + // TODO: use Words.SHORTCUT when we target JellyBean or above final static String SHORTCUT = "shortcut"; - private static final String[] PROJECTION_QUERY = { - Words.WORD, - SHORTCUT, - Words.FREQUENCY, - }; + private static final String[] PROJECTION_QUERY; + static { + // 16 is JellyBean, but we want this to compile against ICS. + if (android.os.Build.VERSION.SDK_INT >= 16) { + PROJECTION_QUERY = new String[] { + Words.WORD, + SHORTCUT, + Words.FREQUENCY, + }; + } else { + PROJECTION_QUERY = new String[] { + Words.WORD, + Words.FREQUENCY, + }; + } + } private static final String NAME = "userunigram"; @@ -58,9 +72,14 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { public UserBinaryDictionary(final Context context, final String locale, final boolean alsoUseMoreRestrictiveLocales) { - super(context, getFilenameWithLocale(NAME, locale), Suggest.DIC_USER); + super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER); if (null == locale) throw new NullPointerException(); // Catch the error earlier - mLocale = locale; + if (SubtypeLocale.NO_LANGUAGE.equals(locale)) { + // If we don't have a locale, insert into the "all locales" user dictionary. + mLocale = USER_DICTIONARY_ALL_LANGUAGES; + } else { + mLocale = locale; + } mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales; // Perform a managed query. The Activity will handle closing and re-querying the cursor // when needed. @@ -136,7 +155,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { requestArguments = localeElements; } final Cursor cursor = mContext.getContentResolver().query( - Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null); + Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null); try { addWords(cursor); } finally { @@ -182,16 +201,18 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { } private void addWords(Cursor cursor) { + // 16 is JellyBean, but we want this to compile against ICS. + final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16; clearFusionDictionary(); if (cursor == null) return; if (cursor.moveToFirst()) { final int indexWord = cursor.getColumnIndex(Words.WORD); - final int indexShortcut = cursor.getColumnIndex(SHORTCUT); + final int indexShortcut = hasShortcutColumn ? cursor.getColumnIndex(SHORTCUT) : 0; final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY); while (!cursor.isAfterLast()) { - String word = cursor.getString(indexWord); - String shortcut = cursor.getString(indexShortcut); - int frequency = cursor.getInt(indexFrequency); + final String word = cursor.getString(indexWord); + final String shortcut = hasShortcutColumn ? cursor.getString(indexShortcut) : null; + final int frequency = cursor.getInt(indexFrequency); // Safeguard against adding really long words. if (word.length() < MAX_WORD_LENGTH) { super.addWord(word, null, frequency); diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java deleted file mode 100644 index c1efadd44..000000000 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.content.ContentProviderClient; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.database.ContentObserver; -import android.database.Cursor; -import android.provider.UserDictionary.Words; -import android.text.TextUtils; - -import com.android.inputmethod.keyboard.ProximityInfo; - -import java.util.Arrays; - -// TODO: This class is superseded by {@link UserBinaryDictionary}. Should be cleaned up. -/** - * An expandable dictionary that stores the words in the user unigram dictionary. - * - * @deprecated Use {@link UserBinaryDictionary}. - */ -@Deprecated -public class UserDictionary extends ExpandableDictionary { - - // TODO: use Words.SHORTCUT when it's public in the SDK - final static String SHORTCUT = "shortcut"; - private static final String[] PROJECTION_QUERY = { - Words.WORD, - SHORTCUT, - Words.FREQUENCY, - }; - - // This is not exported by the framework so we pretty much have to write it here verbatim - private static final String ACTION_USER_DICTIONARY_INSERT = - "com.android.settings.USER_DICTIONARY_INSERT"; - - private ContentObserver mObserver; - final private String mLocale; - final private boolean mAlsoUseMoreRestrictiveLocales; - - public UserDictionary(final Context context, final String locale) { - this(context, locale, false); - } - - public UserDictionary(final Context context, final String locale, - final boolean alsoUseMoreRestrictiveLocales) { - super(context, Suggest.DIC_USER); - if (null == locale) throw new NullPointerException(); // Catch the error earlier - mLocale = locale; - mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales; - // Perform a managed query. The Activity will handle closing and re-querying the cursor - // when needed. - ContentResolver cres = context.getContentResolver(); - - mObserver = new ContentObserver(null) { - @Override - public void onChange(boolean self) { - setRequiresReload(true); - } - }; - cres.registerContentObserver(Words.CONTENT_URI, true, mObserver); - - loadDictionary(); - } - - @Override - public synchronized void close() { - if (mObserver != null) { - getContext().getContentResolver().unregisterContentObserver(mObserver); - mObserver = null; - } - super.close(); - } - - @Override - public void loadDictionaryAsync() { - // Split the locale. For example "en" => ["en"], "de_DE" => ["de", "DE"], - // "en_US_foo_bar_qux" => ["en", "US", "foo_bar_qux"] because of the limit of 3. - // This is correct for locale processing. - // For this example, we'll look at the "en_US_POSIX" case. - final String[] localeElements = - TextUtils.isEmpty(mLocale) ? new String[] {} : mLocale.split("_", 3); - final int length = localeElements.length; - - final StringBuilder request = new StringBuilder("(locale is NULL)"); - String localeSoFar = ""; - // At start, localeElements = ["en", "US", "POSIX"] ; localeSoFar = "" ; - // and request = "(locale is NULL)" - for (int i = 0; i < length; ++i) { - // i | localeSoFar | localeElements - // 0 | "" | ["en", "US", "POSIX"] - // 1 | "en_" | ["en", "US", "POSIX"] - // 2 | "en_US_" | ["en", "en_US", "POSIX"] - localeElements[i] = localeSoFar + localeElements[i]; - localeSoFar = localeElements[i] + "_"; - // i | request - // 0 | "(locale is NULL)" - // 1 | "(locale is NULL) or (locale=?)" - // 2 | "(locale is NULL) or (locale=?) or (locale=?)" - request.append(" or (locale=?)"); - } - // At the end, localeElements = ["en", "en_US", "en_US_POSIX"]; localeSoFar = en_US_POSIX_" - // and request = "(locale is NULL) or (locale=?) or (locale=?) or (locale=?)" - - final String[] requestArguments; - // If length == 3, we already have all the arguments we need (common prefix is meaningless - // inside variants - if (mAlsoUseMoreRestrictiveLocales && length < 3) { - request.append(" or (locale like ?)"); - // The following creates an array with one more (null) position - final String[] localeElementsWithMoreRestrictiveLocalesIncluded = - Arrays.copyOf(localeElements, length + 1); - localeElementsWithMoreRestrictiveLocalesIncluded[length] = - localeElements[length - 1] + "_%"; - requestArguments = localeElementsWithMoreRestrictiveLocalesIncluded; - // If for example localeElements = ["en"] - // then requestArguments = ["en", "en_%"] - // and request = (locale is NULL) or (locale=?) or (locale like ?) - // If localeElements = ["en", "en_US"] - // then requestArguments = ["en", "en_US", "en_US_%"] - } else { - requestArguments = localeElements; - } - final Cursor cursor = getContext().getContentResolver() - .query(Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), - requestArguments, null); - try { - addWords(cursor); - } finally { - if (null != cursor) cursor.close(); - } - } - - public boolean isEnabled() { - final ContentResolver cr = getContext().getContentResolver(); - final ContentProviderClient client = cr.acquireContentProviderClient(Words.CONTENT_URI); - if (client != null) { - client.release(); - return true; - } else { - return false; - } - } - - /** - * Adds a word to the user dictionary and makes it persistent. - * - * This will call upon the system interface to do the actual work through the intent - * readied by the system to this effect. - * - * @param word the word to add. If the word is capitalized, then the dictionary will - * recognize it as a capitalized word when searched. - * @param frequency the frequency of occurrence of the word. A frequency of 255 is considered - * the highest. - * @TODO use a higher or float range for frequency - */ - public synchronized void addWordToUserDictionary(final String word, final int frequency) { - // Force load the dictionary here synchronously - if (getRequiresReload()) loadDictionaryAsync(); - // TODO: do something for the UI. With the following, any sufficiently long word will - // look like it will go to the user dictionary but it won't. - // Safeguard against adding long words. Can cause stack overflow. - if (word.length() >= getMaxWordLength()) return; - - // TODO: Add an argument to the intent to specify the frequency. - Intent intent = new Intent(ACTION_USER_DICTIONARY_INSERT); - intent.putExtra(Words.WORD, word); - intent.putExtra(Words.LOCALE, mLocale); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getContext().startActivity(intent); - } - - @Override - public synchronized void getWords(final WordComposer codes, - final CharSequence prevWordForBigrams, final WordCallback callback, - final ProximityInfo proximityInfo) { - super.getWords(codes, prevWordForBigrams, callback, proximityInfo); - } - - @Override - public synchronized boolean isValidWord(CharSequence word) { - return super.isValidWord(word); - } - - private void addWords(Cursor cursor) { - clearDictionary(); - if (cursor == null) return; - final int maxWordLength = getMaxWordLength(); - if (cursor.moveToFirst()) { - final int indexWord = cursor.getColumnIndex(Words.WORD); - final int indexShortcut = cursor.getColumnIndex(SHORTCUT); - final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY); - while (!cursor.isAfterLast()) { - String word = cursor.getString(indexWord); - String shortcut = cursor.getString(indexShortcut); - int frequency = cursor.getInt(indexFrequency); - // Safeguard against adding really long words. Stack may overflow due - // to recursion - if (word.length() < maxWordLength) { - super.addWord(word, null, frequency); - } - if (null != shortcut && shortcut.length() < maxWordLength) { - super.addWord(shortcut, word, frequency); - } - cursor.moveToNext(); - } - } - } -} diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index 5095f6582..3bb670c9a 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -27,9 +27,12 @@ import android.os.AsyncTask; import android.provider.BaseColumns; import android.util.Log; +import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams; import java.lang.ref.SoftReference; +import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; @@ -115,8 +118,7 @@ public class UserHistoryDictionary extends ExpandableDictionary { } public synchronized static UserHistoryDictionary getInstance( - final Context context, final String locale, - final int dictTypeId, final SharedPreferences sp) { + final Context context, final String locale, final SharedPreferences sp) { if (sLangDictCache.containsKey(locale)) { final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale); final UserHistoryDictionary dict = ref == null ? null : ref.get(); @@ -128,14 +130,14 @@ public class UserHistoryDictionary extends ExpandableDictionary { } } final UserHistoryDictionary dict = - new UserHistoryDictionary(context, locale, dictTypeId, sp); + new UserHistoryDictionary(context, locale, sp); sLangDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict)); return dict; } - private UserHistoryDictionary(final Context context, final String locale, final int dicTypeId, - SharedPreferences sp) { - super(context, dicTypeId); + private UserHistoryDictionary(final Context context, final String locale, + final SharedPreferences sp) { + super(context, Dictionary.TYPE_USER_HISTORY); mLocale = locale; mPrefs = sp; if (sOpenHelper == null) { @@ -158,6 +160,14 @@ public class UserHistoryDictionary extends ExpandableDictionary { // super.close(); } + @Override + protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { + // Inhibit suggestions (not predictions) for user history for now. Removing this method + // is enough to use it through the standard ExpandableDictionary way. + return null; + } + /** * Return whether the passed charsequence is in the dictionary. */ diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java index 28847745e..610652ac1 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java @@ -98,11 +98,11 @@ public class UserHistoryDictionaryBigramList { } public HashMap<String, Byte> getBigrams(String word1) { - if (!mBigramMap.containsKey(word1)) { - return EMPTY_BIGRAM_MAP; - } else { - return mBigramMap.get(word1); - } + if (mBigramMap.containsKey(word1)) return mBigramMap.get(word1); + // TODO: lower case according to locale + final String lowerWord1 = word1.toLowerCase(); + if (mBigramMap.containsKey(lowerWord1)) return mBigramMap.get(lowerWord1); + return EMPTY_BIGRAM_MAP; } public boolean removeBigram(String word1, String word2) { diff --git a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java index e5516dc62..1de95d7b8 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java @@ -50,7 +50,7 @@ public class UserHistoryForgettingCurveUtils { } private ForgettingCurveParams(long now, boolean isValid) { - this((int)pushCount((byte)0, isValid), now, now, isValid); + this(pushCount((byte)0, isValid), now, now, isValid); } /** This constructor is called when the user history bigram dictionary is being restored. */ @@ -199,20 +199,20 @@ public class UserHistoryForgettingCurveUtils { public static final int[][] SCORE_TABLE = new int[FC_LEVEL_MAX][ELAPSED_TIME_MAX + 1]; static { for (int i = 0; i < FC_LEVEL_MAX; ++i) { - final double initialFreq; + final float initialFreq; if (i >= 2) { - initialFreq = (double)FC_FREQ_MAX; + initialFreq = FC_FREQ_MAX; } else if (i == 1) { - initialFreq = (double)FC_FREQ_MAX / 2; + initialFreq = FC_FREQ_MAX / 2; } else if (i == 0) { - initialFreq = (double)FC_FREQ_MAX / 4; + initialFreq = FC_FREQ_MAX / 4; } else { continue; } for (int j = 0; j < ELAPSED_TIME_MAX; ++j) { - final double elapsedHour = j * ELAPSED_TIME_INTERVAL_HOURS; - final double freq = - initialFreq * Math.pow(initialFreq, elapsedHour / HALF_LIFE_HOURS); + final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS; + final float freq = initialFreq + * NativeUtils.powf(initialFreq, elapsedHours / HALF_LIFE_HOURS); final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq)); SCORE_TABLE[i][j] = intFreq; } diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 4178955bc..8f71de0e7 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -44,10 +44,9 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; -import java.util.Collections; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.Map; public class Utils { private Utils() { @@ -206,18 +205,24 @@ public class Utils { } // Get the current stack trace - public static String getStackTrace() { + public static String getStackTrace(final int limit) { StringBuilder sb = new StringBuilder(); try { throw new RuntimeException(); } catch (RuntimeException e) { StackTraceElement[] frames = e.getStackTrace(); // Start at 1 because the first frame is here and we don't care about it - for (int j = 1; j < frames.length; ++j) sb.append(frames[j].toString() + "\n"); + for (int j = 1; j < frames.length && j < limit + 1; ++j) { + sb.append(frames[j].toString() + "\n"); + } } return sb.toString(); } + public static String getStackTrace() { + return getStackTrace(Integer.MAX_VALUE); + } + public static class UsabilityStudyLogUtils { // TODO: remove code duplication with ResearchLog class private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java index a0de2f970..14476dcf0 100644 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -22,8 +22,11 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.LocaleUtils.RunInLocale; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; @@ -37,7 +40,7 @@ public class WhitelistDictionary extends ExpandableDictionary { // TODO: Conform to the async load contact of ExpandableDictionary public WhitelistDictionary(final Context context, final Locale locale) { - super(context, Suggest.DIC_WHITELIST); + super(context, Dictionary.TYPE_WHITELIST); // TODO: Move whitelist dictionary into main dictionary. final RunInLocale<Void> job = new RunInLocale<Void>() { @Override @@ -88,6 +91,13 @@ public class WhitelistDictionary extends ExpandableDictionary { return null; } + @Override + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { + // Whitelist does not supply any suggestions or predictions. + return null; + } + // See LatinIME#updateSuggestions. This breaks in the (queer) case that the whitelist // lists that word a should autocorrect to word b, and word c would autocorrect to // an upper-cased version of a. In this case, the way this return value is used would diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index ca9caa1d3..46c892afe 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardActionListener; import java.util.Arrays; @@ -34,11 +33,11 @@ public class WordComposer { private static final int N = BinaryDictionary.MAX_WORD_LENGTH; private int[] mPrimaryKeyCodes; - private int[] mXCoordinates; - private int[] mYCoordinates; - private StringBuilder mTypedWord; + private final InputPointers mInputPointers = new InputPointers(); + private final StringBuilder mTypedWord; private CharSequence mAutoCorrection; private boolean mIsResumed; + private boolean mIsBatchMode; // Cache these values for performance private int mCapsCount; @@ -54,28 +53,23 @@ public class WordComposer { public WordComposer() { mPrimaryKeyCodes = new int[N]; mTypedWord = new StringBuilder(N); - mXCoordinates = new int[N]; - mYCoordinates = new int[N]; mAutoCorrection = null; mTrailingSingleQuotesCount = 0; mIsResumed = false; + mIsBatchMode = false; refreshSize(); } public WordComposer(WordComposer source) { - init(source); - } - - public void init(WordComposer source) { mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length); mTypedWord = new StringBuilder(source.mTypedWord); - mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length); - mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length); + mInputPointers.copy(source.mInputPointers); mCapsCount = source.mCapsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; mAutoCapitalized = source.mAutoCapitalized; mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; mIsResumed = source.mIsResumed; + mIsBatchMode = source.mIsBatchMode; refreshSize(); } @@ -89,10 +83,11 @@ public class WordComposer { mIsFirstCharCapitalized = false; mTrailingSingleQuotesCount = 0; mIsResumed = false; + mIsBatchMode = false; refreshSize(); } - public final void refreshSize() { + private final void refreshSize() { mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length()); } @@ -116,12 +111,8 @@ public class WordComposer { return mPrimaryKeyCodes[index]; } - public int[] getXCoordinates() { - return mXCoordinates; - } - - public int[] getYCoordinates() { - return mYCoordinates; + public InputPointers getInputPointers() { + return mInputPointers; } private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) { @@ -129,36 +120,18 @@ public class WordComposer { return previous && !Character.isUpperCase(codePoint); } - // TODO: remove input keyDetector - public void add(int primaryCode, int x, int y, KeyDetector keyDetector) { - final int keyX; - final int keyY; - if (null == keyDetector - || x == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE - || y == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE - || x == KeyboardActionListener.NOT_A_TOUCH_COORDINATE - || y == KeyboardActionListener.NOT_A_TOUCH_COORDINATE) { - keyX = x; - keyY = y; - } else { - keyX = keyDetector.getTouchX(x); - keyY = keyDetector.getTouchY(y); - } - add(primaryCode, keyX, keyY); - } - /** * Add a new keystroke, with the pressed key's code point with the touch point coordinates. */ - private void add(int primaryCode, int keyX, int keyY) { + public void add(int primaryCode, int keyX, int keyY) { final int newIndex = size(); mTypedWord.appendCodePoint(primaryCode); refreshSize(); if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE ? Character.toLowerCase(primaryCode) : primaryCode; - mXCoordinates[newIndex] = keyX; - mYCoordinates[newIndex] = keyY; + // TODO: Set correct pointer id and time + mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0); } mIsFirstCharCapitalized = isFirstCharCapitalized( newIndex, primaryCode, mIsFirstCharCapitalized); @@ -171,17 +144,22 @@ public class WordComposer { mAutoCorrection = null; } + // TODO: We may want to have appendBatchInputPointers() as well. + public void setBatchInputPointers(InputPointers batchPointers) { + mInputPointers.copy(batchPointers); + mIsBatchMode = true; + } + /** * Internal method to retrieve reasonable proximity info for a character. */ private void addKeyInfo(final int codePoint, final Keyboard keyboard) { - for (final Key key : keyboard.mKeys) { - if (key.mCode == codePoint) { - final int x = key.mX + key.mWidth / 2; - final int y = key.mY + key.mHeight / 2; - add(codePoint, x, y); - return; - } + final Key key = keyboard.getKey(codePoint); + if (key != null) { + final int x = key.mX + key.mWidth / 2; + final int y = key.mY + key.mHeight / 2; + add(codePoint, x, y); + return; } add(codePoint, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } @@ -318,19 +296,17 @@ public class WordComposer { // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate // the last composed word to ensure this does not happen. final int[] primaryKeyCodes = mPrimaryKeyCodes; - final int[] xCoordinates = mXCoordinates; - final int[] yCoordinates = mYCoordinates; mPrimaryKeyCodes = new int[N]; - mXCoordinates = new int[N]; - mYCoordinates = new int[N]; final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes, - xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode, + mInputPointers, mTypedWord.toString(), committedWord, separatorCode, prevWord); + mInputPointers.reset(); if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) { lastComposedWord.deactivate(); } mTypedWord.setLength(0); + mTrailingSingleQuotesCount = 0; refreshSize(); mAutoCorrection = null; mIsResumed = false; @@ -339,12 +315,15 @@ public class WordComposer { public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) { mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes; - mXCoordinates = lastComposedWord.mXCoordinates; - mYCoordinates = lastComposedWord.mYCoordinates; + mInputPointers.set(lastComposedWord.mInputPointers); mTypedWord.setLength(0); mTypedWord.append(lastComposedWord.mTypedWord); refreshSize(); mAutoCorrection = null; // This will be filled by the next call to updateSuggestion. mIsResumed = true; } + + public boolean isBatchMode() { + return mIsBatchMode; + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 89c59f809..2c3eee74c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -787,9 +787,9 @@ public class BinaryDictInputOutput { // (discretizedFrequency + 0.5) times this value to get the median value of the step, // which is the best approximation. This is how we get the most precise result with // only four bits. - final double stepSize = - (double)(MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5 + MAX_BIGRAM_FREQUENCY); - final double firstStepStart = 1 + unigramFrequency + (stepSize / 2.0); + final float stepSize = + (MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5f + MAX_BIGRAM_FREQUENCY); + final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f); final int discretizedFrequency = (int)((bigramFrequency - firstStepStart) / stepSize); // If the bigram freq is less than half-a-step higher than the unigram freq, we get -1 // here. The best approximation would be the unigram freq itself, so we should not diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 88efc5a85..3bdfe1f27 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -20,30 +20,22 @@ import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.service.textservice.SpellCheckerService; -import android.text.TextUtils; import android.util.Log; -import android.util.LruCache; -import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; -import android.view.textservice.TextInfo; -import com.android.inputmethod.compat.SuggestionsInfoCompatUtils; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.ContactsBinaryDictionary; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.Dictionary.WordCallback; import com.android.inputmethod.latin.DictionaryCollection; import com.android.inputmethod.latin.DictionaryFactory; -import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StringUtils; import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary; -import com.android.inputmethod.latin.SynchronouslyLoadedContactsDictionary; import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary; -import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; +import com.android.inputmethod.latin.UserBinaryDictionary; import com.android.inputmethod.latin.WhitelistDictionary; -import com.android.inputmethod.latin.WordComposer; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -66,18 +58,18 @@ public class AndroidSpellCheckerService extends SpellCheckerService public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts"; - private static final int CAPITALIZE_NONE = 0; // No caps, or mixed case - private static final int CAPITALIZE_FIRST = 1; // First only - private static final int CAPITALIZE_ALL = 2; // All caps + public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case + public static final int CAPITALIZE_FIRST = 1; // First only + public static final int CAPITALIZE_ALL = 2; // All caps private final static String[] EMPTY_STRING_ARRAY = new String[0]; private Map<String, DictionaryPool> mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); - private Map<String, Dictionary> mUserDictionaries = - Collections.synchronizedMap(new TreeMap<String, Dictionary>()); + private Map<String, UserBinaryDictionary> mUserDictionaries = + Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>()); private Map<String, Dictionary> mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); - private Dictionary mContactsDictionary; + private ContactsBinaryDictionary mContactsDictionary; // The threshold for a candidate to be offered as a suggestion. private float mSuggestionThreshold; @@ -92,8 +84,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService public static final int SCRIPT_LATIN = 0; public static final int SCRIPT_CYRILLIC = 1; - private static final String SINGLE_QUOTE = "\u0027"; - private static final String APOSTROPHE = "\u2019"; + public static final String SINGLE_QUOTE = "\u0027"; + public static final String APOSTROPHE = "\u2019"; private static final TreeMap<String, Integer> mLanguageToScript; static { // List of the supported languages and their associated script. We won't check @@ -130,7 +122,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY); } - private static int getScriptFromLocale(final Locale locale) { + public static int getScriptFromLocale(final Locale locale) { final Integer script = mLanguageToScript.get(locale.getLanguage()); if (null == script) { throw new RuntimeException("We have been called with an unsupported language: \"" @@ -154,13 +146,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService private void startUsingContactsDictionaryLocked() { if (null == mContactsDictionary) { - if (LatinIME.USE_BINARY_CONTACTS_DICTIONARY) { - // TODO: use the right locale for each session - mContactsDictionary = - new SynchronouslyLoadedContactsBinaryDictionary(this, Locale.getDefault()); - } else { - mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this); - } + // TODO: use the right locale for each session + mContactsDictionary = + new SynchronouslyLoadedContactsBinaryDictionary(this, Locale.getDefault()); } final Iterator<WeakReference<DictionaryCollection>> iterator = mDictionaryCollectionsList.iterator(); @@ -196,19 +184,27 @@ public class AndroidSpellCheckerService extends SpellCheckerService @Override public Session createSession() { - return new AndroidSpellCheckerSession(this); + // Should not refer to AndroidSpellCheckerSession directly considering + // that AndroidSpellCheckerSession may be overlaid. + return AndroidSpellCheckerSessionFactory.newInstance(this); } - private static SuggestionsInfo getNotInDictEmptySuggestions() { + public static SuggestionsInfo getNotInDictEmptySuggestions() { return new SuggestionsInfo(0, EMPTY_STRING_ARRAY); } - private static SuggestionsInfo getInDictEmptySuggestions() { + public static SuggestionsInfo getInDictEmptySuggestions() { return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, EMPTY_STRING_ARRAY); } - private static class SuggestionsGatherer implements WordCallback { + public SuggestionsGatherer newSuggestionsGatherer(final String text, int maxLength) { + return new SuggestionsGatherer( + text, mSuggestionThreshold, mRecommendedThreshold, maxLength); + } + + // TODO: remove this class and replace it by storage local to the session. + public static class SuggestionsGatherer { public static class Result { public final String[] mSuggestions; public final boolean mHasRecommendedSuggestions; @@ -242,9 +238,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService mScores = new int[mMaxLength]; } - @Override - synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score, - int dicTypeId, int dataType) { + synchronized public boolean addWord(char[] word, int[] spaceIndices, int wordOffset, + int wordLength, int score) { final int positionIndex = Arrays.binarySearch(mScores, 0, mLength, score); // binarySearch returns the index if the element exists, and -<insertion index> - 1 // if it doesn't. See documentation for binarySearch. @@ -368,8 +363,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService private void closeAllDictionaries() { final Map<String, DictionaryPool> oldPools = mDictionaryPools; mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); - final Map<String, Dictionary> oldUserDictionaries = mUserDictionaries; - mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); + final Map<String, UserBinaryDictionary> oldUserDictionaries = mUserDictionaries; + mUserDictionaries = + Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>()); final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries; mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); new Thread("spellchecker_close_dicts") { @@ -389,7 +385,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService // The synchronously loaded contacts dictionary should have been in one // or several pools, but it is shielded against multiple closing and it's // safe to call it several times. - final Dictionary dictToClose = mContactsDictionary; + final ContactsBinaryDictionary dictToClose = mContactsDictionary; // TODO: revert to the concrete type when USE_BINARY_CONTACTS_DICTIONARY // is no longer needed mContactsDictionary = null; @@ -400,7 +396,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService }.start(); } - private DictionaryPool getDictionaryPool(final String locale) { + public DictionaryPool getDictionaryPool(final String locale) { DictionaryPool pool = mDictionaryPools.get(locale); if (null == pool) { final Locale localeObject = LocaleUtils.constructLocaleFromString(locale); @@ -421,13 +417,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService DictionaryFactory.createMainDictionaryFromManager(this, locale, true /* useFullEditDistance */); final String localeStr = locale.toString(); - Dictionary userDictionary = mUserDictionaries.get(localeStr); + UserBinaryDictionary userDictionary = mUserDictionaries.get(localeStr); if (null == userDictionary) { - if (LatinIME.USE_BINARY_USER_DICTIONARY) { - userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true); - } else { - userDictionary = new SynchronouslyLoadedUserDictionary(this, localeStr, true); - } + userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true); mUserDictionaries.put(localeStr, userDictionary); } dictionaryCollection.addDictionary(userDictionary); @@ -440,17 +432,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService synchronized (mUseContactsLock) { if (mUseContactsDictionary) { if (null == mContactsDictionary) { - // TODO: revert to the concrete type when USE_BINARY_CONTACTS_DICTIONARY is no - // longer needed - if (LatinIME.USE_BINARY_CONTACTS_DICTIONARY) { - // TODO: use the right locale. We can't do it right now because the - // spell checker is reusing the contacts dictionary across sessions - // without regard for their locale, so we need to fix that first. - mContactsDictionary = new SynchronouslyLoadedContactsBinaryDictionary(this, - Locale.getDefault()); - } else { - mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this); - } + // TODO: use the right locale. We can't do it right now because the + // spell checker is reusing the contacts dictionary across sessions + // without regard for their locale, so we need to fix that first. + mContactsDictionary = new SynchronouslyLoadedContactsBinaryDictionary(this, + Locale.getDefault()); } } dictionaryCollection.addDictionary(mContactsDictionary); @@ -461,7 +447,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService } // This method assumes the text is not empty or null. - private static int getCapitalizationType(String text) { + public static int getCapitalizationType(String text) { // If the first char is not uppercase, then the word is either all lower case, // and in either case we return CAPITALIZE_NONE. if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE; @@ -478,358 +464,4 @@ public class AndroidSpellCheckerService extends SpellCheckerService if (1 == capsCount) return CAPITALIZE_FIRST; return (len == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE); } - - private static class AndroidSpellCheckerSession extends Session { - // Immutable, but need the locale which is not available in the constructor yet - private DictionaryPool mDictionaryPool; - // Likewise - private Locale mLocale; - // Cache this for performance - private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now. - - private final AndroidSpellCheckerService mService; - - private final SuggestionsCache mSuggestionsCache = new SuggestionsCache(); - - private static class SuggestionsParams { - public final String[] mSuggestions; - public final int mFlags; - public SuggestionsParams(String[] suggestions, int flags) { - mSuggestions = suggestions; - mFlags = flags; - } - } - - private static class SuggestionsCache { - private static final int MAX_CACHE_SIZE = 50; - // TODO: support bigram - private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache = - new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE); - - public SuggestionsParams getSuggestionsFromCache(String query) { - return mUnigramSuggestionsInfoCache.get(query); - } - - public void putSuggestionsToCache(String query, String[] suggestions, int flags) { - if (suggestions == null || TextUtils.isEmpty(query)) { - return; - } - mUnigramSuggestionsInfoCache.put(query, new SuggestionsParams(suggestions, flags)); - } - } - - AndroidSpellCheckerSession(final AndroidSpellCheckerService service) { - mService = service; - } - - @Override - public void onCreate() { - final String localeString = getLocale(); - mDictionaryPool = mService.getDictionaryPool(localeString); - mLocale = LocaleUtils.constructLocaleFromString(localeString); - mScript = getScriptFromLocale(mLocale); - } - - /* - * Returns whether the code point is a letter that makes sense for the specified - * locale for this spell checker. - * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml - * and is limited to EFIGS languages and Russian. - * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters - * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters. - */ - private static boolean isLetterCheckableByLanguage(final int codePoint, - final int script) { - switch (script) { - case SCRIPT_LATIN: - // Our supported latin script dictionaries (EFIGS) at the moment only include - // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode - // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, - // so the below is a very efficient way to test for it. As for the 0-0x3F, it's - // excluded from isLetter anyway. - return codePoint <= 0x2AF && Character.isLetter(codePoint); - case SCRIPT_CYRILLIC: - // All Cyrillic characters are in the 400~52F block. There are some in the upper - // Unicode range, but they are archaic characters that are not used in modern - // russian and are not used by our dictionary. - return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); - default: - // Should never come here - throw new RuntimeException("Impossible value of script: " + script); - } - } - - /** - * Finds out whether a particular string should be filtered out of spell checking. - * - * This will loosely match URLs, numbers, symbols. To avoid always underlining words that - * we know we will never recognize, this accepts a script identifier that should be one - * of the SCRIPT_* constants defined above, to rule out quickly characters from very - * different languages. - * - * @param text the string to evaluate. - * @param script the identifier for the script this spell checker recognizes - * @return true if we should filter this text out, false otherwise - */ - private static boolean shouldFilterOut(final String text, final int script) { - if (TextUtils.isEmpty(text) || text.length() <= 1) return true; - - // TODO: check if an equivalent processing can't be done more quickly with a - // compiled regexp. - // Filter by first letter - final int firstCodePoint = text.codePointAt(0); - // Filter out words that don't start with a letter or an apostrophe - if (!isLetterCheckableByLanguage(firstCodePoint, script) - && '\'' != firstCodePoint) return true; - - // Filter contents - final int length = text.length(); - int letterCount = 0; - for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { - final int codePoint = text.codePointAt(i); - // Any word containing a '@' is probably an e-mail address - // Any word containing a '/' is probably either an ad-hoc combination of two - // words or a URI - in either case we don't want to spell check that - if ('@' == codePoint || '/' == codePoint) return true; - if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; - } - // Guestimate heuristic: perform spell checking if at least 3/4 of the characters - // in this word are letters - return (letterCount * 4 < length * 3); - } - - private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote( - TextInfo ti, SentenceSuggestionsInfo ssi) { - final String typedText = ti.getText(); - if (!typedText.contains(SINGLE_QUOTE)) { - return null; - } - final int N = ssi.getSuggestionsCount(); - final ArrayList<Integer> additionalOffsets = new ArrayList<Integer>(); - final ArrayList<Integer> additionalLengths = new ArrayList<Integer>(); - final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = - new ArrayList<SuggestionsInfo>(); - for (int i = 0; i < N; ++i) { - final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i); - final int flags = si.getSuggestionsAttributes(); - if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) { - continue; - } - final int offset = ssi.getOffsetAt(i); - final int length = ssi.getLengthAt(i); - final String subText = typedText.substring(offset, offset + length); - if (!subText.contains(SINGLE_QUOTE)) { - continue; - } - final String[] splitTexts = subText.split(SINGLE_QUOTE, -1); - if (splitTexts == null || splitTexts.length <= 1) { - continue; - } - final int splitNum = splitTexts.length; - for (int j = 0; j < splitNum; ++j) { - final String splitText = splitTexts[j]; - if (TextUtils.isEmpty(splitText)) { - continue; - } - if (mSuggestionsCache.getSuggestionsFromCache(splitText) == null) { - continue; - } - final int newLength = splitText.length(); - // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO - final int newFlags = 0; - final SuggestionsInfo newSi = new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY); - newSi.setCookieAndSequence(si.getCookie(), si.getSequence()); - if (DBG) { - Log.d(TAG, "Override and remove old span over: " - + splitText + ", " + offset + "," + newLength); - } - additionalOffsets.add(offset); - additionalLengths.add(newLength); - additionalSuggestionsInfos.add(newSi); - } - } - final int additionalSize = additionalOffsets.size(); - if (additionalSize <= 0) { - return null; - } - final int suggestionsSize = N + additionalSize; - final int[] newOffsets = new int[suggestionsSize]; - final int[] newLengths = new int[suggestionsSize]; - final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize]; - int i; - for (i = 0; i < N; ++i) { - newOffsets[i] = ssi.getOffsetAt(i); - newLengths[i] = ssi.getLengthAt(i); - newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i); - } - for (; i < suggestionsSize; ++i) { - newOffsets[i] = additionalOffsets.get(i - N); - newLengths[i] = additionalLengths.get(i - N); - newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N); - } - return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths); - } - - @Override - public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple( - TextInfo[] textInfos, int suggestionsLimit) { - final SentenceSuggestionsInfo[] retval = super.onGetSentenceSuggestionsMultiple( - textInfos, suggestionsLimit); - if (retval == null || retval.length != textInfos.length) { - return retval; - } - for (int i = 0; i < retval.length; ++i) { - final SentenceSuggestionsInfo tempSsi = - fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]); - if (tempSsi != null) { - retval[i] = tempSsi; - } - } - return retval; - } - - @Override - public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, - int suggestionsLimit, boolean sequentialWords) { - final int length = textInfos.length; - final SuggestionsInfo[] retval = new SuggestionsInfo[length]; - for (int i = 0; i < length; ++i) { - final String prevWord; - if (sequentialWords && i > 0) { - final String prevWordCandidate = textInfos[i - 1].getText(); - // Note that an empty string would be used to indicate the initial word - // in the future. - prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate; - } else { - prevWord = null; - } - retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit); - retval[i].setCookieAndSequence( - textInfos[i].getCookie(), textInfos[i].getSequence()); - } - return retval; - } - - // Note : this must be reentrant - /** - * Gets a list of suggestions for a specific string. This returns a list of possible - * corrections for the text passed as an argument. It may split or group words, and - * even perform grammatical analysis. - */ - @Override - public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, - final int suggestionsLimit) { - return onGetSuggestions(textInfo, null, suggestionsLimit); - } - - private SuggestionsInfo onGetSuggestions( - final TextInfo textInfo, final String prevWord, final int suggestionsLimit) { - try { - final String inText = textInfo.getText(); - final SuggestionsParams cachedSuggestionsParams = - mSuggestionsCache.getSuggestionsFromCache(inText); - if (cachedSuggestionsParams != null) { - if (DBG) { - Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags); - } - return new SuggestionsInfo( - cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions); - } - - if (shouldFilterOut(inText, mScript)) { - DictAndProximity dictInfo = null; - try { - dictInfo = mDictionaryPool.takeOrGetNull(); - if (null == dictInfo) return getNotInDictEmptySuggestions(); - return dictInfo.mDictionary.isValidWord(inText) ? - getInDictEmptySuggestions() : getNotInDictEmptySuggestions(); - } finally { - if (null != dictInfo) { - if (!mDictionaryPool.offer(dictInfo)) { - Log.e(TAG, "Can't re-insert a dictionary into its pool"); - } - } - } - } - final String text = inText.replaceAll(APOSTROPHE, SINGLE_QUOTE); - - // TODO: Don't gather suggestions if the limit is <= 0 unless necessary - final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text, - mService.mSuggestionThreshold, mService.mRecommendedThreshold, - suggestionsLimit); - final WordComposer composer = new WordComposer(); - final int length = text.length(); - for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { - final int codePoint = text.codePointAt(i); - // The getXYForCodePointAndScript method returns (Y << 16) + X - final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript( - codePoint, mScript); - if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) { - composer.add(codePoint, WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE, null); - } else { - composer.add(codePoint, xy & 0xFFFF, xy >> 16, null); - } - } - - final int capitalizeType = getCapitalizationType(text); - boolean isInDict = true; - DictAndProximity dictInfo = null; - try { - dictInfo = mDictionaryPool.takeOrGetNull(); - if (null == dictInfo) return getNotInDictEmptySuggestions(); - dictInfo.mDictionary.getWords(composer, prevWord, suggestionsGatherer, - dictInfo.mProximityInfo); - isInDict = dictInfo.mDictionary.isValidWord(text); - if (!isInDict && CAPITALIZE_NONE != capitalizeType) { - // We want to test the word again if it's all caps or first caps only. - // If it's fully down, we already tested it, if it's mixed case, we don't - // want to test a lowercase version of it. - isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale)); - } - } finally { - if (null != dictInfo) { - if (!mDictionaryPool.offer(dictInfo)) { - Log.e(TAG, "Can't re-insert a dictionary into its pool"); - } - } - } - - final SuggestionsGatherer.Result result = suggestionsGatherer.getResults( - capitalizeType, mLocale); - - if (DBG) { - Log.i(TAG, "Spell checking results for " + text + " with suggestion limit " - + suggestionsLimit); - Log.i(TAG, "IsInDict = " + isInDict); - Log.i(TAG, "LooksLikeTypo = " + (!isInDict)); - Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions); - if (null != result.mSuggestions) { - for (String suggestion : result.mSuggestions) { - Log.i(TAG, suggestion); - } - } - } - - final int flags = - (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY - : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) - | (result.mHasRecommendedSuggestions - ? SuggestionsInfoCompatUtils - .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS() - : 0); - final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions); - mSuggestionsCache.putSuggestionsToCache(text, result.mSuggestions, flags); - return retval; - } catch (RuntimeException e) { - // Don't kill the keyboard if there is a bug in the spell checker - if (DBG) { - throw e; - } else { - Log.e(TAG, "Exception while spellcheking: " + e); - return getNotInDictEmptySuggestions(); - } - } - } - } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java new file mode 100644 index 000000000..501a0e221 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin.spellcheck; + +import android.text.TextUtils; +import android.util.Log; +import android.view.textservice.SentenceSuggestionsInfo; +import android.view.textservice.SuggestionsInfo; +import android.view.textservice.TextInfo; + +import java.util.ArrayList; + +public class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession { + private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName(); + private static final boolean DBG = false; + private final static String[] EMPTY_STRING_ARRAY = new String[0]; + + public AndroidSpellCheckerSession(AndroidSpellCheckerService service) { + super(service); + } + + private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti, + SentenceSuggestionsInfo ssi) { + final String typedText = ti.getText(); + if (!typedText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { + return null; + } + final int N = ssi.getSuggestionsCount(); + final ArrayList<Integer> additionalOffsets = new ArrayList<Integer>(); + final ArrayList<Integer> additionalLengths = new ArrayList<Integer>(); + final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = + new ArrayList<SuggestionsInfo>(); + String currentWord = null; + for (int i = 0; i < N; ++i) { + final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i); + final int flags = si.getSuggestionsAttributes(); + if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) { + continue; + } + final int offset = ssi.getOffsetAt(i); + final int length = ssi.getLengthAt(i); + final String subText = typedText.substring(offset, offset + length); + final String prevWord = currentWord; + currentWord = subText; + if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { + continue; + } + final String[] splitTexts = + subText.split(AndroidSpellCheckerService.SINGLE_QUOTE, -1); + if (splitTexts == null || splitTexts.length <= 1) { + continue; + } + final int splitNum = splitTexts.length; + for (int j = 0; j < splitNum; ++j) { + final String splitText = splitTexts[j]; + if (TextUtils.isEmpty(splitText)) { + continue; + } + if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) { + continue; + } + final int newLength = splitText.length(); + // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO + final int newFlags = 0; + final SuggestionsInfo newSi = + new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY); + newSi.setCookieAndSequence(si.getCookie(), si.getSequence()); + if (DBG) { + Log.d(TAG, "Override and remove old span over: " + splitText + ", " + + offset + "," + newLength); + } + additionalOffsets.add(offset); + additionalLengths.add(newLength); + additionalSuggestionsInfos.add(newSi); + } + } + final int additionalSize = additionalOffsets.size(); + if (additionalSize <= 0) { + return null; + } + final int suggestionsSize = N + additionalSize; + final int[] newOffsets = new int[suggestionsSize]; + final int[] newLengths = new int[suggestionsSize]; + final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize]; + int i; + for (i = 0; i < N; ++i) { + newOffsets[i] = ssi.getOffsetAt(i); + newLengths[i] = ssi.getLengthAt(i); + newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i); + } + for (; i < suggestionsSize; ++i) { + newOffsets[i] = additionalOffsets.get(i - N); + newLengths[i] = additionalLengths.get(i - N); + newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N); + } + return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths); + } + + @Override + public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, + int suggestionsLimit) { + final SentenceSuggestionsInfo[] retval = + super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit); + if (retval == null || retval.length != textInfos.length) { + return retval; + } + for (int i = 0; i < retval.length; ++i) { + final SentenceSuggestionsInfo tempSsi = + fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]); + if (tempSsi != null) { + retval[i] = tempSsi; + } + } + return retval; + } + + @Override + public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, + int suggestionsLimit, boolean sequentialWords) { + final int length = textInfos.length; + final SuggestionsInfo[] retval = new SuggestionsInfo[length]; + for (int i = 0; i < length; ++i) { + final String prevWord; + if (sequentialWords && i > 0) { + final String prevWordCandidate = textInfos[i - 1].getText(); + // Note that an empty string would be used to indicate the initial word + // in the future. + prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate; + } else { + prevWord = null; + } + retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit); + retval[i].setCookieAndSequence(textInfos[i].getCookie(), + textInfos[i].getSequence()); + } + return retval; + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java new file mode 100644 index 000000000..8eb1eb68e --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin.spellcheck; + +import android.service.textservice.SpellCheckerService.Session; + +public abstract class AndroidSpellCheckerSessionFactory { + public static Session newInstance(AndroidSpellCheckerService service) { + return new AndroidSpellCheckerSession(service); + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java new file mode 100644 index 000000000..0171dc06d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin.spellcheck; + +import android.service.textservice.SpellCheckerService.Session; +import android.text.TextUtils; +import android.util.Log; +import android.util.LruCache; +import android.view.textservice.SuggestionsInfo; +import android.view.textservice.TextInfo; + +import com.android.inputmethod.compat.SuggestionsInfoCompatUtils; +import com.android.inputmethod.latin.LocaleUtils; +import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer; + +import java.util.ArrayList; +import java.util.Locale; + +public abstract class AndroidWordLevelSpellCheckerSession extends Session { + private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName(); + private static final boolean DBG = false; + + // Immutable, but need the locale which is not available in the constructor yet + private DictionaryPool mDictionaryPool; + // Likewise + private Locale mLocale; + // Cache this for performance + private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now. + private final AndroidSpellCheckerService mService; + protected final SuggestionsCache mSuggestionsCache = new SuggestionsCache(); + + private static class SuggestionsParams { + public final String[] mSuggestions; + public final int mFlags; + public SuggestionsParams(String[] suggestions, int flags) { + mSuggestions = suggestions; + mFlags = flags; + } + } + + protected static class SuggestionsCache { + private static final char CHAR_DELIMITER = '\uFFFC'; + private static final int MAX_CACHE_SIZE = 50; + private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache = + new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE); + + // TODO: Support n-gram input + private static String generateKey(String query, String prevWord) { + if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWord)) { + return query; + } + return query + CHAR_DELIMITER + prevWord; + } + + // TODO: Support n-gram input + public SuggestionsParams getSuggestionsFromCache(String query, String prevWord) { + return mUnigramSuggestionsInfoCache.get(generateKey(query, prevWord)); + } + + // TODO: Support n-gram input + public void putSuggestionsToCache( + String query, String prevWord, String[] suggestions, int flags) { + if (suggestions == null || TextUtils.isEmpty(query)) { + return; + } + mUnigramSuggestionsInfoCache.put( + generateKey(query, prevWord), new SuggestionsParams(suggestions, flags)); + } + } + + AndroidWordLevelSpellCheckerSession(final AndroidSpellCheckerService service) { + mService = service; + } + + @Override + public void onCreate() { + final String localeString = getLocale(); + mDictionaryPool = mService.getDictionaryPool(localeString); + mLocale = LocaleUtils.constructLocaleFromString(localeString); + mScript = AndroidSpellCheckerService.getScriptFromLocale(mLocale); + } + + /* + * Returns whether the code point is a letter that makes sense for the specified + * locale for this spell checker. + * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml + * and is limited to EFIGS languages and Russian. + * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters + * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters. + */ + private static boolean isLetterCheckableByLanguage(final int codePoint, + final int script) { + switch (script) { + case AndroidSpellCheckerService.SCRIPT_LATIN: + // Our supported latin script dictionaries (EFIGS) at the moment only include + // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode + // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, + // so the below is a very efficient way to test for it. As for the 0-0x3F, it's + // excluded from isLetter anyway. + return codePoint <= 0x2AF && Character.isLetter(codePoint); + case AndroidSpellCheckerService.SCRIPT_CYRILLIC: + // All Cyrillic characters are in the 400~52F block. There are some in the upper + // Unicode range, but they are archaic characters that are not used in modern + // russian and are not used by our dictionary. + return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); + default: + // Should never come here + throw new RuntimeException("Impossible value of script: " + script); + } + } + + /** + * Finds out whether a particular string should be filtered out of spell checking. + * + * This will loosely match URLs, numbers, symbols. To avoid always underlining words that + * we know we will never recognize, this accepts a script identifier that should be one + * of the SCRIPT_* constants defined above, to rule out quickly characters from very + * different languages. + * + * @param text the string to evaluate. + * @param script the identifier for the script this spell checker recognizes + * @return true if we should filter this text out, false otherwise + */ + private static boolean shouldFilterOut(final String text, final int script) { + if (TextUtils.isEmpty(text) || text.length() <= 1) return true; + + // TODO: check if an equivalent processing can't be done more quickly with a + // compiled regexp. + // Filter by first letter + final int firstCodePoint = text.codePointAt(0); + // Filter out words that don't start with a letter or an apostrophe + if (!isLetterCheckableByLanguage(firstCodePoint, script) + && '\'' != firstCodePoint) return true; + + // Filter contents + final int length = text.length(); + int letterCount = 0; + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { + final int codePoint = text.codePointAt(i); + // Any word containing a '@' is probably an e-mail address + // Any word containing a '/' is probably either an ad-hoc combination of two + // words or a URI - in either case we don't want to spell check that + if ('@' == codePoint || '/' == codePoint) return true; + if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; + } + // Guestimate heuristic: perform spell checking if at least 3/4 of the characters + // in this word are letters + return (letterCount * 4 < length * 3); + } + + // Note : this must be reentrant + /** + * Gets a list of suggestions for a specific string. This returns a list of possible + * corrections for the text passed as an argument. It may split or group words, and + * even perform grammatical analysis. + */ + @Override + public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, + final int suggestionsLimit) { + return onGetSuggestions(textInfo, null, suggestionsLimit); + } + + protected SuggestionsInfo onGetSuggestions( + final TextInfo textInfo, final String prevWord, final int suggestionsLimit) { + try { + final String inText = textInfo.getText(); + final SuggestionsParams cachedSuggestionsParams = + mSuggestionsCache.getSuggestionsFromCache(inText, prevWord); + if (cachedSuggestionsParams != null) { + if (DBG) { + Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags); + } + return new SuggestionsInfo( + cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions); + } + + if (shouldFilterOut(inText, mScript)) { + DictAndProximity dictInfo = null; + try { + dictInfo = mDictionaryPool.takeOrGetNull(); + if (null == dictInfo) { + return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + } + return dictInfo.mDictionary.isValidWord(inText) + ? AndroidSpellCheckerService.getInDictEmptySuggestions() + : AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + } finally { + if (null != dictInfo) { + if (!mDictionaryPool.offer(dictInfo)) { + Log.e(TAG, "Can't re-insert a dictionary into its pool"); + } + } + } + } + final String text = inText.replaceAll( + AndroidSpellCheckerService.APOSTROPHE, AndroidSpellCheckerService.SINGLE_QUOTE); + + // TODO: Don't gather suggestions if the limit is <= 0 unless necessary + //final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text, + //mService.mSuggestionThreshold, mService.mRecommendedThreshold, + //suggestionsLimit); + final SuggestionsGatherer suggestionsGatherer = mService.newSuggestionsGatherer( + text, suggestionsLimit); + final WordComposer composer = new WordComposer(); + final int length = text.length(); + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { + final int codePoint = text.codePointAt(i); + // The getXYForCodePointAndScript method returns (Y << 16) + X + final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript( + codePoint, mScript); + if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) { + composer.add(codePoint, WordComposer.NOT_A_COORDINATE, + WordComposer.NOT_A_COORDINATE); + } else { + composer.add(codePoint, xy & 0xFFFF, xy >> 16); + } + } + + final int capitalizeType = AndroidSpellCheckerService.getCapitalizationType(text); + boolean isInDict = true; + DictAndProximity dictInfo = null; + try { + dictInfo = mDictionaryPool.takeOrGetNull(); + if (null == dictInfo) { + return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + } + final ArrayList<SuggestedWordInfo> suggestions = + dictInfo.mDictionary.getSuggestions(composer, prevWord, + dictInfo.mProximityInfo); + for (final SuggestedWordInfo suggestion : suggestions) { + final String suggestionStr = suggestion.mWord.toString(); + suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0, + suggestionStr.length(), suggestion.mScore); + } + isInDict = dictInfo.mDictionary.isValidWord(text); + if (!isInDict && AndroidSpellCheckerService.CAPITALIZE_NONE != capitalizeType) { + // We want to test the word again if it's all caps or first caps only. + // If it's fully down, we already tested it, if it's mixed case, we don't + // want to test a lowercase version of it. + isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale)); + } + } finally { + if (null != dictInfo) { + if (!mDictionaryPool.offer(dictInfo)) { + Log.e(TAG, "Can't re-insert a dictionary into its pool"); + } + } + } + + final SuggestionsGatherer.Result result = suggestionsGatherer.getResults( + capitalizeType, mLocale); + + if (DBG) { + Log.i(TAG, "Spell checking results for " + text + " with suggestion limit " + + suggestionsLimit); + Log.i(TAG, "IsInDict = " + isInDict); + Log.i(TAG, "LooksLikeTypo = " + (!isInDict)); + Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions); + if (null != result.mSuggestions) { + for (String suggestion : result.mSuggestions) { + Log.i(TAG, suggestion); + } + } + } + + final int flags = + (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY + : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) + | (result.mHasRecommendedSuggestions + ? SuggestionsInfoCompatUtils + .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS() + : 0); + final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions); + mSuggestionsCache.putSuggestionsToCache(text, prevWord, result.mSuggestions, flags); + return retval; + } catch (RuntimeException e) { + // Don't kill the keyboard if there is a bug in the spell checker + if (DBG) { + throw e; + } else { + Log.e(TAG, "Exception while spellcheking: " + e); + return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java index e86390b11..4d33f4ba5 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java @@ -57,11 +57,11 @@ import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.ViewLayoutUtils; +import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.ResearchLogger; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; -import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; @@ -71,7 +71,7 @@ import java.util.ArrayList; public class SuggestionsView extends RelativeLayout implements OnClickListener, OnLongClickListener { public interface Listener { - public boolean addWordToDictionary(String word); + public boolean addWordToUserDictionary(String word); public void pickSuggestionManually(int index, CharSequence word, int x, int y); } @@ -336,8 +336,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, if (LatinImeLogger.sDBG && suggestedWords.size() > 1) { // If we auto-correct, then the autocorrection is in slot 0 and the typed word // is in slot 1. - if (index == mCenterSuggestionIndex && suggestedWords.mHasAutoCorrectionCandidate - && Suggest.shouldBlockAutoCorrectionBySafetyNet( + if (index == mCenterSuggestionIndex + && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet( suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) { return 0xFFFF0000; } @@ -718,10 +718,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mPreviewPopup.dismiss(); } - private void addToDictionary(CharSequence word) { - mListener.addWordToDictionary(word.toString()); - } - private final KeyboardActionListener mMoreSuggestionsListener = new KeyboardActionListener.Adapter() { @Override @@ -863,7 +859,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, @Override public void onClick(View view) { if (mParams.isAddToDictionaryShowing(view)) { - addToDictionary(mParams.getAddToDictionaryWord()); + mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString()); clear(); return; } |