aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/proguard.flags8
-rw-r--r--java/res/values-af/strings.xml7
-rw-r--r--java/res/values-am/strings.xml7
-rw-r--r--java/res/values-ar/strings.xml7
-rw-r--r--java/res/values-be/strings.xml7
-rw-r--r--java/res/values-bg/strings.xml7
-rw-r--r--java/res/values-ca/strings.xml7
-rw-r--r--java/res/values-cs/strings.xml7
-rw-r--r--java/res/values-da/strings.xml14
-rw-r--r--java/res/values-de/strings.xml7
-rw-r--r--java/res/values-el/strings.xml7
-rw-r--r--java/res/values-en-rGB/strings.xml7
-rw-r--r--java/res/values-es-rUS/strings.xml7
-rw-r--r--java/res/values-es/strings.xml7
-rw-r--r--java/res/values-et/strings.xml7
-rw-r--r--java/res/values-fa/strings.xml7
-rw-r--r--java/res/values-fi/strings.xml7
-rw-r--r--java/res/values-fr/strings.xml7
-rw-r--r--java/res/values-hi/strings.xml7
-rw-r--r--java/res/values-hr/strings.xml7
-rw-r--r--java/res/values-hu/strings.xml7
-rw-r--r--java/res/values-in/strings.xml7
-rw-r--r--java/res/values-it/strings.xml7
-rw-r--r--java/res/values-iw/strings.xml7
-rw-r--r--java/res/values-ja/strings.xml7
-rw-r--r--java/res/values-ko/strings.xml7
-rw-r--r--java/res/values-lt/strings.xml7
-rw-r--r--java/res/values-lv/strings.xml7
-rw-r--r--java/res/values-ms/strings.xml7
-rw-r--r--java/res/values-nb/strings.xml7
-rw-r--r--java/res/values-nl/strings.xml7
-rw-r--r--java/res/values-pl/strings.xml7
-rw-r--r--java/res/values-pt-rPT/strings.xml7
-rw-r--r--java/res/values-pt/strings.xml7
-rw-r--r--java/res/values-rm/strings.xml14
-rw-r--r--java/res/values-ro/strings.xml7
-rw-r--r--java/res/values-ru/strings.xml7
-rw-r--r--java/res/values-sk/strings.xml7
-rw-r--r--java/res/values-sl/strings.xml7
-rw-r--r--java/res/values-sr/strings.xml7
-rw-r--r--java/res/values-sv/strings.xml7
-rw-r--r--java/res/values-sw/strings.xml7
-rw-r--r--java/res/values-th/strings.xml7
-rw-r--r--java/res/values-tl/strings.xml7
-rw-r--r--java/res/values-tr/strings.xml7
-rw-r--r--java/res/values-uk/strings.xml7
-rw-r--r--java/res/values-vi/strings.xml7
-rw-r--r--java/res/values-zh-rCN/strings.xml7
-rw-r--r--java/res/values-zh-rTW/strings.xml7
-rw-r--r--java/res/values-zu/strings.xml7
-rw-r--r--java/res/values/config.xml3
-rw-r--r--java/res/values/strings.xml16
-rw-r--r--java/res/xml/key_styles_common.xml19
-rw-r--r--java/res/xml/key_styles_f1.xml43
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java88
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java1
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java23
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java17
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionary.java178
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtils.java182
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java709
-rw-r--r--java/src/com/android/inputmethod/latin/NativeUtils.java32
-rw-r--r--java/src/com/android/inputmethod/latin/ResearchLogger.java1196
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java429
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java52
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java10
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java54
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java56
-rw-r--r--java/src/com/android/inputmethod/latin/UserDictionary.java225
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java16
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java12
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java6
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java84
-rw-r--r--native/jni/Android.mk4
-rw-r--r--native/jni/com_android_inputmethod_latin_NativeUtils.cpp40
-rw-r--r--native/jni/com_android_inputmethod_latin_NativeUtils.h29
-rw-r--r--native/jni/jni_common.cpp6
-rw-r--r--native/jni/src/correction.cpp88
-rw-r--r--native/jni/src/correction.h20
-rw-r--r--native/jni/src/defines.h15
-rw-r--r--native/jni/src/proximity_info.cpp268
-rw-r--r--native/jni/src/proximity_info.h108
-rw-r--r--native/jni/src/proximity_info_state.cpp139
-rw-r--r--native/jni/src/proximity_info_state.h219
-rw-r--r--native/jni/src/unigram_dictionary.cpp76
-rw-r--r--native/jni/src/unigram_dictionary.h35
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java10
-rw-r--r--tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java178
-rw-r--r--tests/src/com/android/inputmethod/latin/UtilsTests.java65
-rw-r--r--tools/dicttool/Android.mk25
-rw-r--r--tools/dicttool/etc/Android.mk20
-rwxr-xr-xtools/dicttool/etc/dicttool62
-rw-r--r--tools/dicttool/etc/manifest.txt1
-rw-r--r--tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java94
-rw-r--r--tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java120
-rw-r--r--tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java35
-rw-r--r--tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java7
-rw-r--r--tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java2
-rw-r--r--tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java2
103 files changed, 3116 insertions, 2383 deletions
diff --git a/java/proguard.flags b/java/proguard.flags
index 34e23aa9a..752ced3e3 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -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/values-af/strings.xml b/java/res/values-af/strings.xml
index 7431fced8..22543efe5 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android-speltoetser"</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>
@@ -111,6 +112,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.xml b/java/res/values-am/strings.xml
index d70c05da9..a1b168cb7 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android የፊደል ማረሚያ"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android የፊደል ማረሚያ (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"የፊደል አራሚ ቅንብሮች"</string>
@@ -111,6 +112,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..2a2207447 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"التدقيق الإملائي في Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"التدقيق الإملائي في Android‏ (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"إعدادات التدقيق الإملائي"</string>
@@ -111,6 +112,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.xml b/java/res/values-be/strings.xml
index 4d14565b9..ff3213ad6 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Iнструмент праверкi правапiсу для Android"</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>
@@ -111,6 +112,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..5e916cef0 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Програма за правописна проверка за Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Програма за правописна проверка за Android (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройки за проверка на правописа"</string>
@@ -111,6 +112,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.xml b/java/res/values-ca/strings.xml
index e3adbcff4..1b7cfc255 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortogràfic d\'Android"</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>
@@ -111,6 +112,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 f93421c4f..ffe2b2b3b 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-da/strings.xml
index 50b0b0a9f..99ceca2ee 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -24,6 +24,8 @@
<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>
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontrol"</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>
@@ -111,6 +113,18 @@
<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>
+ <!-- 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 />
<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.xml b/java/res/values-de/strings.xml
index 216aac5d9..5ddd309c4 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android-Rechtschreibprüfung"</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>
@@ -111,6 +112,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.xml b/java/res/values-el/strings.xml
index 486346a09..5dcbf43ba 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Ορθογραφικός έλεγχος Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Ορθογραφικός έλεγχος Android (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"Ρυθμίσεις ορθογραφικού ελέγχου"</string>
@@ -111,6 +112,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.xml b/java/res/values-en-rGB/strings.xml
index 502007666..cd9b218e7 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android spell checker"</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>
@@ -111,6 +112,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..39ca40903 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortográfico de Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-es/strings.xml
index 3c60aa6e2..79a36cccd 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector de Android"</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>
@@ -111,6 +112,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..45870d3a9 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Androidi õigekirjakontroll"</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>
@@ -111,6 +112,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.xml b/java/res/values-fa/strings.xml
index bc0e85b77..9f0e5aaa4 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"فرمان‌های گزارش‌گیری پژوهش"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"غلط‌گیر املای Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"غلط‌گیر املای Android (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"تنظیمات غلط گیری املایی"</string>
@@ -115,6 +116,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.xml b/java/res/values-fi/strings.xml
index 97002edfe..f4dff35b9 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android-oikoluku"</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>
@@ -111,6 +112,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.xml b/java/res/values-fr/strings.xml
index 285d2226c..1094c4ac3 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Correcteur orthographique Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-hi/strings.xml
index 2b1808460..c131e1278 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android वर्तनी परीक्षक"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android वर्तनी परीक्षक (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"वर्तनी जांच सेटिंग"</string>
@@ -111,6 +112,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.xml b/java/res/values-hr/strings.xml
index a59d4698f..f8a198df2 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Androidova provjera pravopisa"</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>
@@ -111,6 +112,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..fc1ac1227 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Androidos helyesírás-ellenőrző"</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>
@@ -111,6 +112,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..a7a2c0383 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Pemeriksa ejaan Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-it/strings.xml
index 647350dff..892abac10 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Controllo ortografico Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-iw/strings.xml
index 323cac198..000c9f337 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"בודק האיות של Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"בודק האיות של Android ‏(AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"הגדרות בדיקת איות"</string>
@@ -111,6 +112,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..05dd3a364 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Androidスペルチェッカー"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidスペルチェッカー(AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"スペルチェックの設定"</string>
@@ -111,6 +112,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..6a998838a 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android 맞춤법 검사기"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 맞춤법 검사기(AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"맞춤법 검사 설정"</string>
@@ -111,6 +112,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..86bfc3714 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"„Android“ rašybos tikrinimo programa"</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>
@@ -111,6 +112,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.xml b/java/res/values-lv/strings.xml
index 93727a8ba..403e0f2d1 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android pareizrakstības pārbaudītājs"</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>
@@ -111,6 +112,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..60f5a4133 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Penyemak ejaan Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-nb/strings.xml
index 8ed23763a..fc2bd5539 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontroll"</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>
@@ -111,6 +112,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.xml b/java/res/values-nl/strings.xml
index a7d499bd0..26d5e3f20 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Spellingcontrole van Android"</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>
@@ -111,6 +112,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..dc4f396a7 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Słownik Androida"</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>
@@ -111,6 +112,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..35eedd82a 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Verificador ortográfico do Android"</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>
@@ -111,6 +112,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..a12fc23e1 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Corretor ortográfico do Android"</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>
@@ -111,6 +112,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..c6c936e49 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -26,6 +26,8 @@
<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 english_ime_research_log (8492602295696577851) -->
+ <skip />
<!-- no translation found for spell_checker_service_name (7338064335159755926) -->
<skip />
<!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
@@ -190,6 +192,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..523a855c7 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Verificator ortografic Android"</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>
@@ -111,6 +112,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 a9bd6ace1..34fdb0f5c 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Проверка правописания Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Проверка правописания Android (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройка проверки правописания"</string>
@@ -111,6 +112,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..41fc0cf24 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-sl/strings.xml
index ce7dc70bb..f18de78bc 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Črkovalnik za Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-sr/strings.xml
index 6d2899b47..11e678ebc 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android провера правописа"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android провера правописа (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"Подешавања провере правописа"</string>
@@ -111,6 +112,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.xml b/java/res/values-sv/strings.xml
index 75e80d42d..bf1e5054f 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Stavningskontroll i Android"</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>
@@ -111,6 +112,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.xml b/java/res/values-sw/strings.xml
index 82c867e3f..bd3b15052 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Kikagua tahajia cha Android"</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>
@@ -111,6 +112,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 uliyorekodiwa"</string>
+ <string name="do_not_log_this_session" msgid="413762473641146336">"Usifungue 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..6f25a2024 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"แอนดรอยด์ตรวจสอบการสะกด"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"แอนดรอยด์ตรวจสอบการสะกด (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"การตั้งค่าการตรวจสอบการสะกด"</string>
@@ -111,6 +112,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..e2fe6c5b4 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Pang-check ng pagbabaybay ng Android"</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>
@@ -111,6 +112,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 39737f55e..f8f3c3341 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android yazım denetleyici"</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>
@@ -111,6 +112,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.xml b/java/res/values-uk/strings.xml
index e994e1a15..c2e743b45 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Засіб перевірки орфографії Android"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Засіб перевірки орфографії Android (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"Налаштування перевірки орфографії"</string>
@@ -111,6 +112,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..9ce2d5a78 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Trình kiểm tra chính tả Android"</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>
@@ -111,6 +112,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..f5e38f935 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"探究日志命令"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼写检查工具"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼写检查工具 (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"拼写检查设置"</string>
@@ -111,6 +112,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..9179e669b 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼字檢查"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼字檢查 (AOSP)"</string>
<string name="android_spell_checker_settings" msgid="5822324635435443689">"拼字檢查設定"</string>
@@ -111,6 +112,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.xml b/java/res/values-zu/strings.xml
index d3f80e42a..3f7c96b66 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -24,6 +24,7 @@
<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="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string>
<string name="spell_checker_service_name" msgid="7338064335159755926">"Isihloli sokupela se-Android"</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>
@@ -111,6 +112,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 d5268ea5f..e20061d7d 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -23,7 +23,8 @@
<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
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index d51d3789a..0b781af79 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -26,6 +26,8 @@
<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>
@@ -233,6 +235,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>
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/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 21f175d7d..6fc630d05 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -89,7 +89,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 +102,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;
@@ -422,67 +424,67 @@ public class Keyboard {
* This class parses Keyboard XML file and eventually build a Keyboard.
* The Keyboard XML file looks like:
* <pre>
- * &gt;!-- xml/keyboard.xml --&lt;
- * &gt;Keyboard keyboard_attributes*&lt;
- * &gt;!-- Keyboard Content --&lt;
- * &gt;Row row_attributes*&lt;
- * &gt;!-- Row Content --&lt;
- * &gt;Key key_attributes* /&lt;
- * &gt;Spacer horizontalGap="32.0dp" /&lt;
- * &gt;include keyboardLayout="@xml/other_keys"&lt;
+ * &lt;!-- xml/keyboard.xml --&gt;
+ * &lt;Keyboard keyboard_attributes*&gt;
+ * &lt;!-- Keyboard Content --&gt;
+ * &lt;Row row_attributes*&gt;
+ * &lt;!-- Row Content --&gt;
+ * &lt;Key key_attributes* /&gt;
+ * &lt;Spacer horizontalGap="32.0dp" /&gt;
+ * &lt;include keyboardLayout="@xml/other_keys"&gt;
* ...
- * &gt;/Row&lt;
- * &gt;include keyboardLayout="@xml/other_rows"&lt;
+ * &lt;/Row&gt;
+ * &lt;include keyboardLayout="@xml/other_rows"&gt;
* ...
- * &gt;/Keyboard&lt;
+ * &lt;/Keyboard&gt;
* </pre>
- * The XML file which is included in other file must have &gt;merge&lt; as root element,
+ * The XML file which is included in other file must have &lt;merge&gt; as root element,
* such as:
* <pre>
- * &gt;!-- xml/other_keys.xml --&lt;
- * &gt;merge&lt;
- * &gt;Key key_attributes* /&lt;
+ * &lt;!-- xml/other_keys.xml --&gt;
+ * &lt;merge&gt;
+ * &lt;Key key_attributes* /&gt;
* ...
- * &gt;/merge&lt;
+ * &lt;/merge&gt;
* </pre>
* and
* <pre>
- * &gt;!-- xml/other_rows.xml --&lt;
- * &gt;merge&lt;
- * &gt;Row row_attributes*&lt;
- * &gt;Key key_attributes* /&lt;
- * &gt;/Row&lt;
+ * &lt;!-- xml/other_rows.xml --&gt;
+ * &lt;merge&gt;
+ * &lt;Row row_attributes*&gt;
+ * &lt;Key key_attributes* /&gt;
+ * &lt;/Row&gt;
* ...
- * &gt;/merge&lt;
+ * &lt;/merge&gt;
* </pre>
* You can also use switch-case-default tags to select Rows and Keys.
* <pre>
- * &gt;switch&lt;
- * &gt;case case_attribute*&lt;
- * &gt;!-- Any valid tags at switch position --&lt;
- * &gt;/case&lt;
+ * &lt;switch&gt;
+ * &lt;case case_attribute*&gt;
+ * &lt;!-- Any valid tags at switch position --&gt;
+ * &lt;/case&gt;
* ...
- * &gt;default&lt;
- * &gt;!-- Any valid tags at switch position --&lt;
- * &gt;/default&lt;
- * &gt;/switch&lt;
+ * &lt;default&gt;
+ * &lt;!-- Any valid tags at switch position --&gt;
+ * &lt;/default&gt;
+ * &lt;/switch&gt;
* </pre>
* You can declare Key style and specify styles within Key tags.
* <pre>
- * &gt;switch&lt;
- * &gt;case mode="email"&lt;
- * &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+ * &lt;switch&gt;
+ * &lt;case mode="email"&gt;
+ * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
* keyLabel=".com"
- * /&lt;
- * &gt;/case&lt;
- * &gt;case mode="url"&lt;
- * &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+ * /&gt;
+ * &lt;/case&gt;
+ * &lt;case mode="url"&gt;
+ * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
* keyLabel="http://"
- * /&lt;
- * &gt;/case&lt;
- * &gt;/switch&lt;
+ * /&gt;
+ * &lt;/case&gt;
+ * &lt;/switch&gt;
* ...
- * &gt;Key keyStyle="shift-key" ... /&lt;
+ * &lt;Key keyStyle="shift-key" ... /&gt;
* </pre>
*/
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 51a0f537f..18e01fb49 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -873,6 +873,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/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index babf6ec99..34e428e82 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -242,10 +242,6 @@ public class PointerTracker {
+ " ignoreModifier=" + ignoreModifierKey
+ " enabled=" + key.isEnabled());
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(key,
- ignoreModifierKey);
- }
if (ignoreModifierKey) {
return false;
}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 1207c3fcd..1bc825479 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -27,6 +27,8 @@ 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 +77,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);
}
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/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/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 34308dfb3..10e511eaf 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;
/**
@@ -61,6 +64,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) {
super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId);
+ mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context);
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/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/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 34a92fd30..4a5471c85 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -514,8 +514,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(
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 1f2982aed..b3a0f54b9 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -56,7 +56,6 @@ 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;
@@ -103,27 +102,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,7 +121,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 SettingsValues mCurrentSettings;
private InputAttributes mInputAttributes;
private View mExtractArea;
@@ -162,15 +140,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();
// 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,7 +179,6 @@ 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;
@@ -211,6 +186,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private int mDelayUpdateSuggestions;
private int mDelayUpdateShiftState;
private long mDoubleSpacesTurnIntoPeriodTimeout;
+ private long mDoubleSpaceTimerStart;
public UIHandler(LatinIME outerInstance) {
super(outerInstance);
@@ -275,16 +251,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
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 +369,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 +387,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();
@@ -457,11 +431,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
@Override
protected SettingsValues job(Resources res) {
- return new SettingsValues(mPrefs, LatinIME.this);
+ return new SettingsValues(mPrefs, mInputAttributes, 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 +443,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 +451,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
oldContactsDictionary = null;
}
mSuggest = new Suggest(this, subtypeLocale);
- if (mSettingsValues.mAutoCorrectEnabled) {
- mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+ if (mCurrentSettings.isCorrectionOn()) {
+ 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();
- }
+ mUserDictionary = new UserBinaryDictionary(this, localeStr);
+ mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
mSuggest.setUserDictionary(mUserDictionary);
resetContactsDictionary(oldContactsDictionary);
@@ -505,36 +474,37 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
/**
* 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, Suggest.DIC_CONTACTS, 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, Suggest.DIC_CONTACTS, locale);
}
}
@@ -569,9 +539,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(getCurrentInputConnection());
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ mConnection.finishComposingText();
+ mConnection.endBatchEdit();
if (isShowingOptionDialog())
mOptionsDialog.dismiss();
}
@@ -660,6 +631,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)) {
@@ -709,14 +681,12 @@ 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.isCorrectionOn()) {
+ mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
}
- switcher.loadKeyboard(editorInfo, mSettingsValues);
+ switcher.loadKeyboard(editorInfo, mCurrentSettings);
if (mSuggestionsView != null)
mSuggestionsView.clear();
@@ -726,19 +696,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.postUpdateSuggestions();
mHandler.cancelDoubleSpacesTimer();
- inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
- mSettingsValues.mKeyPreviewPopupDismissDelay);
+ inputView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
+ mCurrentSettings.mKeyPreviewPopupDismissDelay);
inputView.setProximityCorrectionEnabled(true);
if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
}
+ @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 +723,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();
@@ -768,7 +746,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 +757,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.
@@ -888,7 +872,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
}
- if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
+ if (null != mInputAttributes && mInputAttributes.mApplicationSpecifiedCompletionOn) {
mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
if (applicationSpecifiedCompletions == null) {
clearSuggestions();
@@ -1001,7 +985,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;
}
@@ -1021,10 +1005,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void resetEntireInputState() {
resetComposingState(true /* alsoResetLastComposedWord */);
updateSuggestions();
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.finishComposingText();
- }
+ mConnection.finishComposingText();
}
private void resetComposingState(final boolean alsoResetLastComposedWord) {
@@ -1033,15 +1014,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
}
- public void commitTyped(final InputConnection ic, final int separatorCode) {
+ public 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(
@@ -1052,7 +1031,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
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 +1049,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 +1073,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.mCorrectionMode == Suggest.CORRECTION_NONE) 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,26 +1105,9 @@ 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);
- }
- }
- }
-
@Override
public boolean addWordToDictionary(String word) {
- if (USE_BINARY_USER_DICTIONARY) {
- ((UserBinaryDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
- } else {
- ((UserDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
- }
+ mUserDictionary.addWordToUserDictionary(word, 128);
// Suggestion strip should be updated after the operation of adding word to the
// user dictionary
mHandler.postUpdateSuggestions();
@@ -1193,17 +1150,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 +1175,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 +1193,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 +1219,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mDeleteCount = 0;
}
mLastKeyTime = when;
+ mConnection.beginBatchEdit(getCurrentInputConnection());
if (ProductionFlag.IS_EXPERIMENTAL) {
- if (ResearchLogger.sIsLogging) {
- ResearchLogger.getInstance().logKeyEvent(primaryCode, x, y);
- }
+ ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
}
final KeyboardSwitcher switcher = mKeyboardSwitcher;
@@ -1321,6 +1271,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
case Keyboard.CODE_LANGUAGE_SWITCH:
handleLanguageSwitchKey();
break;
+ case Keyboard.CODE_RESEARCH:
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().presentResearchDialog(this);
+ }
+ break;
default:
if (primaryCode == Keyboard.CODE_TAB
&& mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT) {
@@ -1328,7 +1283,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
break;
}
mSpaceState = SPACE_STATE_NONE;
- if (mSettingsValues.isWordSeparator(primaryCode)) {
+ if (mCurrentSettings.isWordSeparator(primaryCode)) {
didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
} else {
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
@@ -1349,23 +1304,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
&& primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
mLastComposedWord.deactivate();
mEnteredText = null;
+ mConnection.endBatchEdit();
}
@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(getCurrentInputConnection());
+ 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 +1327,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetComposingState(true /* alsoResetLastComposedWord */);
}
- // ic may not be null
- private CharSequence specificTldProcessingOnTextInput(final InputConnection ic,
- final CharSequence text) {
+ 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 +1336,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());
@@ -1400,24 +1352,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);
}
@@ -1431,7 +1374,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int length = mWordComposer.size();
if (length > 0) {
mWordComposer.deleteLast();
- ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+ mConnection.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()) {
@@ -1442,7 +1385,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.postUpdateSuggestions();
}
} else {
- ic.deleteSurroundingText(1, 0);
+ mConnection.deleteSurroundingText(1, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1);
}
@@ -1450,17 +1393,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 +1415,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 +1434,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);
+ 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 +1477,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 +1492,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))
+ && isSuggestionsRequested() &&
+ !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
@@ -1576,23 +1510,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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);
+ // 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);
mHandler.postUpdateSuggestions();
} 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
@@ -1619,37 +1551,32 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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;
+ final boolean shouldAutoCorrect = mCurrentSettings.isCorrectionOn();
if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
- commitCurrentAutoCorrection(primaryCode, ic);
+ 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 (maybeDoubleSpace()) {
mSpaceState = SPACE_STATE_DOUBLE;
} else if (!isShowingPunctuationList()) {
mSpaceState = SPACE_STATE_WEAK;
@@ -1657,13 +1584,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mHandler.startDoubleSpacesTimer();
- if (!isCursorTouchingWord()) {
+ if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
mHandler.cancelUpdateSuggestions();
mHandler.postUpdateBigramPredictions();
}
} 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,7 +1619,7 @@ 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)
@@ -1703,19 +1627,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
public boolean isSuggestionsRequested() {
- return mInputAttributes.mIsSettingsSuggestionStripOn
- && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
+ // TODO: move this method to mSettingsValues
+ return (null != mInputAttributes && mInputAttributes.mIsSettingsSuggestionStripOn)
+ && (mCurrentSettings.isCorrectionOn() || isShowingSuggestionsStrip());
}
public boolean isShowingPunctuationList() {
if (mSuggestionsView == null) return false;
- return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions();
+ return mCurrentSettings.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.isSuggestionStripVisibleInOrientation(mDisplayOrientation);
}
public boolean isSuggestionsStripVisible() {
@@ -1725,7 +1648,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return true;
if (!isShowingSuggestionsStrip())
return false;
- if (mInputAttributes.mApplicationSpecifiedCompletionOn)
+ if (null != mInputAttributes && mInputAttributes.mApplicationSpecifiedCompletionOn)
return true;
return isSuggestionsRequested();
}
@@ -1765,14 +1688,12 @@ 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);
}
}
@@ -1795,18 +1716,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// TODO: May need a better way of retrieving previous word
- final InputConnection ic = getCurrentInputConnection();
- final CharSequence prevWord;
- if (null == ic) {
- prevWord = null;
- } else {
- prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
- }
-
+ final CharSequence prevWord = mConnection.getPreviousWord(mCurrentSettings.mWordSeparators);
final CharSequence typedWord = mWordComposer.getTypedWord();
// getSuggestedWords handles gracefully a null value of prevWord
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
- prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode);
+ prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
+ mCurrentSettings.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
@@ -1820,7 +1735,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
showSuggestions(suggestedWords, typedWord);
} else {
SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
- if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
+ if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
previousSuggestions = SuggestedWords.EMPTY;
}
final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
@@ -1856,8 +1771,7 @@ 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();
@@ -1878,10 +1792,11 @@ 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));
}
}
@@ -1889,15 +1804,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@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()) {
@@ -1917,13 +1824,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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 ((null != mInputAttributes && mInputAttributes.mApplicationSpecifiedCompletionOn)
&& mApplicationSpecifiedCompletions != null
&& index >= 0 && index < mApplicationSpecifiedCompletions.length) {
if (mSuggestionsView != null) {
@@ -1931,13 +1838,13 @@ 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.beginBatchEdit(getCurrentInputConnection());
+ mConnection.commitCompletion(completionInfo);
+ mConnection.endBatchEdit();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
+ completionInfo.getText(), x, y);
}
return;
}
@@ -1987,7 +1894,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
if (mIsUserDictionaryAvailable) {
mSuggestionsView.showAddToDictionaryHint(
- suggestion, mSettingsValues.mHintToSaveText);
+ suggestion, mCurrentSettings.mHintToSaveText);
} else {
mHandler.postUpdateSuggestions();
}
@@ -1999,22 +1906,16 @@ 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);
- }
- }
+ if (mCurrentSettings.mEnableSuggestionSpanInsertion) {
+ final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
+ mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
+ this, chosenWord, suggestedWords, mIsMainDictionaryAvailable),
+ 1);
+ } else {
+ mConnection.commitText(chosenWord, 1);
+ }
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_commitText(chosenWord);
}
// Add the word to the user history dictionary
final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
@@ -2030,15 +1931,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mSuggest == null || !isSuggestionsRequested())
return;
- if (!mSettingsValues.mBigramPredictionEnabled) {
+ if (!mCurrentSettings.mBigramPredictionEnabled) {
setPunctuationSuggestions();
return;
}
final SuggestedWords suggestedWords;
- if (mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
- final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
- mSettingsValues.mWordSeparators);
+ if (mCurrentSettings.mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
+ final CharSequence prevWord = mConnection.getThisWord(mCurrentSettings.mWordSeparators);
if (!TextUtils.isEmpty(prevWord)) {
suggestedWords = mSuggest.getBigramPredictions(prevWord);
} else {
@@ -2058,7 +1958,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
public void setPunctuationSuggestions() {
- setSuggestions(mSettingsValues.mSuggestPuncList, false);
+ setSuggestions(mCurrentSettings.mSuggestPuncList, false);
setAutoCorrectionIndicator(false);
setSuggestionStripShown(isSuggestionsStripVisible());
}
@@ -2069,19 +1969,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// 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 (!mCurrentSettings.isCorrectionOn()) 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 CharSequence prevWord
+ = mConnection.getPreviousWord(mCurrentSettings.mWordSeparators);
final String secondWord;
if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
secondWord = suggestion.toString().toLowerCase(
@@ -2101,88 +1993,29 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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);
+ mConnection.setComposingText(word, 1);
mHandler.postUpdateSuggestions();
}
- // "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;
@@ -2196,7 +2029,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 "
@@ -2204,7 +2037,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,9 +2049,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// 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);
+ mConnection.setComposingText(originallyTypedWord, 1);
} else {
- ic.commitText(originallyTypedWord, 1);
+ mConnection.commitText(originallyTypedWord, 1);
// Re-insert the separator
sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
@@ -2234,61 +2067,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertSwapPunctuation();
- }
- ic.endBatchEdit();
- return true;
- }
-
public boolean isWordSeparator(int code) {
- return mSettingsValues.isWordSeparator(code);
+ return mCurrentSettings.isWordSeparator(code);
}
public boolean preferCapitalization() {
@@ -2302,15 +2082,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// 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()) {
+ if (mConnection.isCursorTouchingWord(mCurrentSettings)) {
mHandler.postUpdateSuggestions();
} else {
mHandler.postUpdateBigramPredictions();
@@ -2348,12 +2127,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);
}
}
}
@@ -2371,25 +2147,6 @@ 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);
}
@@ -2436,10 +2193,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;
@@ -2467,12 +2224,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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(" mCorrectionMode=" + mCurrentSettings.mCorrectionMode);
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(" isCorrectionOn=" + mCurrentSettings.isCorrectionOn());
+ p.println(" mSoundOn=" + mCurrentSettings.mSoundOn);
+ p.println(" mVibrateOn=" + mCurrentSettings.mVibrateOn);
+ p.println(" mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn);
p.println(" mInputAttributes=" + mInputAttributes.toString());
}
}
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..46efa78f1 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java
@@ -16,37 +16,45 @@
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.Date;
+import java.util.Locale;
import java.util.Map;
+import java.util.UUID;
/**
* Logs the use of the LatinIME keyboard.
@@ -58,198 +66,73 @@ 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;
+ /* 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);
- 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";
+ // 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 String DEFAULT_FILENAME = "researchLog.txt";
- private static final long LOGFILE_PURGE_INTERVAL = 1000 * 60 * 60 * 24;
+ 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
- protected InputMethodService mIms;
- protected File mFile;
- protected PrintWriter mPrintWriter;
+ 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;
- /* package */ LogFileManager() {
- }
+ // set when LatinIME should ignore an onUpdateSelection() callback that
+ // arises from operations in this class
+ private static boolean sLatinIMEExpectingUpdateSelection = false;
- public void init(final InputMethodService ims) {
- mIms = ims;
+ private static class NullOutputStream extends OutputStream {
+ /** {@inheritDoc} */
+ @Override
+ public void write(byte[] buffer, int offset, int count) {
+ // nop
}
- public synchronized void createLogFile() throws IOException {
- createLogFile(DEFAULT_FILENAME);
+ /** {@inheritDoc} */
+ @Override
+ public void write(byte[] buffer) {
+ // nop
}
- 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 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);
- }
- 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);
- }
- 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;
- }
- 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");
- }
- try {
- createLogFile();
- printWriter = mPrintWriter;
- } catch (IOException e) {
- Log.w(TAG, "Failed to create log file. Not logging.");
- return false;
- }
- }
- 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");
- }
- mFile = null;
- }
- }
-
- public synchronized void close() {
- if (mPrintWriter != null) {
- mPrintWriter.close();
- mPrintWriter = null;
- mFile = null;
- if (DEBUG) {
- Log.d(TAG, "logfile closed");
- }
- }
- }
-
- /* package */ synchronized void flush() {
- if (mPrintWriter != null) {
- mPrintWriter.flush();
- }
- }
-
- /* 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();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return s;
+ @Override
+ public void write(int oneByte) {
}
}
- 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;
+ private ResearchLogger() {
+ mLoggingState = LOGGING_STATE_OFF;
}
public static ResearchLogger getInstance() {
return sInstance;
}
- public static void init(final InputMethodService ims, final SharedPreferences prefs) {
- sInstance.initInternal(ims, prefs);
- }
-
- /* 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();
+ 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.");
}
}
if (prefs != null) {
@@ -258,173 +141,124 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
- /**
- * Represents a category of logging events that share the same subfield structure.
- */
- 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;
+ public synchronized void start() {
+ Log.d(TAG, "start called");
+ if (!sIsLogging) {
+ // Log.w(TAG, "not in usability mode; not logging");
+ return;
}
- 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());
+ 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;
+ }
+ if (mLoggingHandler == null) {
+ mLoggingHandler = new Handler(mHandlerThread.getLooper());
+ mLoggingState = LOGGING_STATE_OFF;
+ }
+ if (mFile == null) {
+ final String timestampString = TIMESTAMP_DATEFORMAT.format(new Date());
+ mFile = new File(mFilesDir, FILENAME_PREFIX + timestampString + FILENAME_SUFFIX);
+ }
+ if (mLoggingState == LOGGING_STATE_OFF) {
+ try {
+ mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
+ mJsonWriter.setLenient(true);
+ mJsonWriter.beginArray();
+ mLoggingState = LOGGING_STATE_ON;
+ } catch (IOException e) {
+ Log.w(TAG, "cannot start JsonWriter");
+ mJsonWriter = NULL_JSON_WRITER;
+ e.printStackTrace();
+ }
+ }
}
}
- 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.");
- }
- // perhaps logfile was deleted. try to recreate and relog.
+ public synchronized void stop() {
+ Log.d(TAG, "stop called");
+ if (mLoggingHandler != null && mLoggingState == LOGGING_STATE_ON) {
+ mLoggingState = LOGGING_STATE_STOPPING;
+ // put this in the Handler queue so pending writes are processed first.
+ mLoggingHandler.post(new Runnable() {
+ @Override
+ public void run() {
try {
- mLogFileManager.createLogFile(PreferenceManager
- .getDefaultSharedPreferences(mIms));
- mLogFileManager.append(s);
+ 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();
+ }
}
}
+ });
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
- });
+ }
}
- public void clearAll() {
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
+ 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, "Delete log file.");
+ Log.d(TAG, "logfile closed");
}
- mLogFileManager.reset();
}
- });
+ }
+ return isLogFileDeleted;
}
- /* package */ LogFileManager getLogFileManager() {
- return mLogFileManager;
+ /* package */ synchronized void flush() {
+ try {
+ mJsonWriter.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
@Override
@@ -433,325 +267,591 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return;
}
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 (sIsLogging == false) {
+ abort();
}
}
- 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);
- }
+ /* 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();
+ }
+ break;
+ }
+ }
+ };
+ final AlertDialog.Builder builder = new AlertDialog.Builder(latinIME)
+ .setItems(items, listener)
+ .setTitle(title);
+ latinIME.showOptionDialog(builder.create());
}
- 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 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 = {};
+
+ /**
+ * Write a description of the event out to the ResearchLog.
+ *
+ * Runs in the background to avoid blocking the UI thread.
+ *
+ * @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 writeEvent(final String[] keys, final Object[] values) {
+ assert values.length + 1 == keys.length;
+ if (mLoggingState == LOGGING_STATE_ON) {
+ mLoggingHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ 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());
+ }
+ 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[] keys = (Key[]) value;
+ mJsonWriter.beginArray();
+ for (Key key : keys) {
+ mJsonWriter.beginObject();
+ mJsonWriter.name("code").value(key.mCode);
+ mJsonWriter.name("altCode").value(key.mAltCode);
+ mJsonWriter.name("x").value(key.mX);
+ mJsonWriter.name("y").value(key.mY);
+ mJsonWriter.name("w").value(key.mWidth);
+ mJsonWriter.name("h").value(key.mHeight);
+ mJsonWriter.endObject();
+ }
+ mJsonWriter.endArray();
+ } else if (value instanceof SuggestedWords) {
+ SuggestedWords words = (SuggestedWords) value;
+ mJsonWriter.beginObject();
+ mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
+ mJsonWriter.name("hasAutoCorrectionCandidate")
+ .value(words.mHasAutoCorrectionCandidate);
+ mJsonWriter.name("isPunctuationSuggestions")
+ .value(words.mIsPunctuationSuggestions);
+ mJsonWriter.name("allowsToBeAutoCorrected")
+ .value(words.mAllowsToBeAutoCorrected);
+ 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;
+ }
+ }
+ }
+ });
+ }
+ }
+
+ 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().writeEvent(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(code), x, y
+ };
+ getInstance().writeEvent(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, before, after, position
+ };
+ getInstance().writeEvent(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 = {
+ typedWord, autoCorrection
+ };
+ getInstance().writeEvent(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 Object[] values = {
+ typedWord.toString()
+ };
+ getInstance().writeEvent(EVENTKEYS_LATINIME_COMMITTEXT, values);
}
+ 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().writeEvent(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().writeEvent(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().writeEvent(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;
+ Object[] values = new Object[2];
+ 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();
}
}
- logUnstructured("LatinIME_onDisplayCompletions", builder.toString());
+ getInstance().writeEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values);
}
}
+ 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().writeEvent(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 Object[] values = {
+ lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart,
+ newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, word
+ };
+ getInstance().writeEvent(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().writeEvent(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
+ };
+ getInstance().writeEvent(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 = {
+ replacedWord, index, suggestion, x, y
+ };
+ getInstance().writeEvent(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().writeEvent(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().writeEvent(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().writeEvent(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 = {
+ code
+ };
+ getInstance().writeEvent(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", "");
- }
+ getInstance().writeEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT,
+ EVENTKEYS_NULLVALUES);
}
+ private static final String[] EVENTKEYS_LATINIME_SWITCHTOKEYBOARDVIEW = {
+ "LatinIMESwitchToKeyboardView"
+ };
public static void latinIME_switchToKeyboardView() {
- if (UnsLogGroup.LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED) {
- final String s = "Switch to keyboard view.";
- logUnstructured("LatinIME_switchToKeyboardView", s);
- }
+ getInstance().writeEvent(EVENTKEYS_LATINIME_SWITCHTOKEYBOARDVIEW, 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().writeEvent(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 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,
+ kid.passwordInput(),
+ kid.mShortcutKeyEnabled,
+ kid.mHasShortcutKey,
+ kid.mLanguageSwitchKeyEnabled,
+ kid.isMultiLine(),
+ keyboard.mOccupiedWidth,
+ keyboard.mOccupiedHeight,
+ keyboard.mKeys
+ };
+ getInstance().writeEvent(EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD, values);
+ }
+ }
+
+ 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().writeEvent(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().writeEvent(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(code), outputText, x, y, ignoreModifierKey, altersCode,
+ key.isEnabled()
+ };
+ getInstance().writeEvent(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(primaryCode), withSliding, ignoreModifierKey,
+ key.isEnabled()
+ };
+ getInstance().writeEvent(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().writeEvent(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().writeEvent(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().writeEvent(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().writeEvent(EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS, values);
}
}
-} \ No newline at end of file
+
+ private static final String[] EVENTKEYS_USER_TIMESTAMP = {
+ "UserTimestamp"
+ };
+ public void userTimestamp() {
+ getInstance().writeEvent(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..0c19bed05
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -0,0 +1,429 @@
+/*
+ * 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.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;
+
+ InputConnection mIC;
+ int mNestLevel;
+ public RichInputConnection() {
+ mIC = null;
+ mNestLevel = 0;
+ }
+
+ public void beginBatchEdit(final InputConnection newInputConnection) {
+ if (++mNestLevel == 1) {
+ mIC = newInputConnection;
+ 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) {
+ if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF;
+ return mIC.getCursorCapsMode(inputType);
+ }
+
+ public CharSequence getTextBeforeCursor(final int i, final int j) {
+ if (null != mIC) return mIC.getTextBeforeCursor(i, j);
+ return null;
+ }
+
+ public CharSequence getTextAfterCursor(final int i, final int j) {
+ 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) {
+ 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 getPreviousWord(final String sentenceSeperators) {
+ //TODO: Should fix this. This could be slow!
+ if (null == mIC) return null;
+ CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
+ return getPreviousWord(prev, sentenceSeperators);
+ }
+
+ /**
+ * 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 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 CharSequence getThisWord(String sentenceSeperators) {
+ if (null == mIC) return null;
+ final CharSequence prev = mIC.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];
+ }
+
+ /**
+ * @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() {
+ 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) {
+ 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 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 = getTextBeforeCursor(1, 0);
+ if (TextUtils.isEmpty(textBeforeCursor)
+ || settings.isWordSeparator(textBeforeCursor.charAt(0))) return null;
+
+ // 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;
+ 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/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index b07c3e59f..f8a0a4df6 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;
@@ -78,12 +91,15 @@ public class SettingsValues {
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 int mCorrectionMode;
+ 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
@@ -151,6 +167,8 @@ public class SettingsValues {
mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray(
getPrefAdditionalSubtypes(prefs, res));
+ mCorrectionMode = createCorrectionMode(inputAttributes);
+ mSuggestionVisibility = createSuggestionVisibility(res);
}
// Helper functions to create member values.
@@ -184,6 +202,23 @@ public class SettingsValues {
return wordSeparators;
}
+ private int createCorrectionMode(final InputAttributes inputAttributes) {
+ final boolean shouldAutoCorrect = mAutoCorrectEnabled
+ && (null == inputAttributes || !inputAttributes.mInputTypeNoAutoCorrect);
+ if (mBigramSuggestionEnabled && shouldAutoCorrect) return Suggest.CORRECTION_FULL_BIGRAM;
+ return shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
+ }
+
+ 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 +226,17 @@ public class SettingsValues {
res.getBoolean(R.bool.config_default_vibration_enabled));
}
+ public boolean isCorrectionOn() {
+ return mCorrectionMode == Suggest.CORRECTION_FULL
+ || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM;
+ }
+
+ 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));
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 336a76f4b..68b7b913f 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -66,7 +66,7 @@ public class Suggest implements Dictionary.WordCallback {
private static final boolean DBG = LatinImeLogger.sDBG;
private boolean mHasMainDictionary;
- private Dictionary mContactsDict;
+ private ContactsBinaryDictionary mContactsDict;
private WhitelistDictionary mWhiteListDictionary;
private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries =
new ConcurrentHashMap<String, Dictionary>();
@@ -148,7 +148,7 @@ public class Suggest implements Dictionary.WordCallback {
return mHasMainDictionary;
}
- public Dictionary getContactsDictionary() {
+ public ContactsBinaryDictionary getContactsDictionary() {
return mContactsDict;
}
@@ -164,7 +164,7 @@ public class Suggest implements Dictionary.WordCallback {
* 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) {
+ public void setUserDictionary(UserBinaryDictionary userDictionary) {
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
}
@@ -173,13 +173,13 @@ 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);
}
- public void setUserHistoryDictionary(Dictionary userHistoryDictionary) {
+ public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM,
userHistoryDictionary);
addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM,
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/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/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/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..f2d21ab9b 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -44,10 +44,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
-import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
-import java.util.Map;
public class Utils {
private Utils() {
@@ -206,18 +204,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/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..7fffc31c0 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -30,18 +30,17 @@ 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;
@@ -73,11 +72,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService
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;
@@ -154,13 +153,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();
@@ -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;
@@ -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);
@@ -501,20 +487,32 @@ public class AndroidSpellCheckerService extends SpellCheckerService
}
private static class SuggestionsCache {
+ private static final char CHAR_DELIMITER = '\uFFFC';
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);
+ // 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));
}
- public void putSuggestionsToCache(String query, String[] suggestions, int flags) {
+ // 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(query, new SuggestionsParams(suggestions, flags));
+ mUnigramSuggestionsInfoCache.put(
+ generateKey(query, prevWord), new SuggestionsParams(suggestions, flags));
}
}
@@ -609,6 +607,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
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();
@@ -618,6 +617,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
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(SINGLE_QUOTE)) {
continue;
}
@@ -631,7 +632,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
if (TextUtils.isEmpty(splitText)) {
continue;
}
- if (mSuggestionsCache.getSuggestionsFromCache(splitText) == null) {
+ if (mSuggestionsCache.getSuggestionsFromCache(
+ splitText, prevWord) == null) {
continue;
}
final int newLength = splitText.length();
@@ -727,7 +729,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
try {
final String inText = textInfo.getText();
final SuggestionsParams cachedSuggestionsParams =
- mSuggestionsCache.getSuggestionsFromCache(inText);
+ mSuggestionsCache.getSuggestionsFromCache(inText, prevWord);
if (cachedSuggestionsParams != null) {
if (DBG) {
Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags);
@@ -819,7 +821,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
.getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
: 0);
final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions);
- mSuggestionsCache.putSuggestionsToCache(text, result.mSuggestions, flags);
+ 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
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index d53757fd4..3bb7b58f1 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH := $(call my-dir)
# If you change any of those flags, you need to rebuild both libjni_latinime_static
# and the shared library.
#FLAG_DBG := true
-#FLAG_DO_PROFILE := true
+FLAG_DO_PROFILE ?= false
######################################
include $(CLEAR_VARS)
@@ -35,6 +35,7 @@ LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
LATIN_IME_JNI_SRC_FILES := \
com_android_inputmethod_keyboard_ProximityInfo.cpp \
com_android_inputmethod_latin_BinaryDictionary.cpp \
+ com_android_inputmethod_latin_NativeUtils.cpp \
jni_common.cpp
LATIN_IME_CORE_SRC_FILES := \
@@ -45,6 +46,7 @@ LATIN_IME_CORE_SRC_FILES := \
correction.cpp \
dictionary.cpp \
proximity_info.cpp \
+ proximity_info_state.cpp \
unigram_dictionary.cpp
LOCAL_SRC_FILES := \
diff --git a/native/jni/com_android_inputmethod_latin_NativeUtils.cpp b/native/jni/com_android_inputmethod_latin_NativeUtils.cpp
new file mode 100644
index 000000000..c1e586a4b
--- /dev/null
+++ b/native/jni/com_android_inputmethod_latin_NativeUtils.cpp
@@ -0,0 +1,40 @@
+/*
+**
+** 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.
+*/
+
+#include "com_android_inputmethod_latin_NativeUtils.h"
+#include "jni.h"
+#include "jni_common.h"
+
+#include <math.h>
+
+namespace latinime {
+
+static float latinime_NativeUtils_powf(float x, float y) {
+ return powf(x, y);
+}
+
+static JNINativeMethod sMethods[] = {
+ {"powf", "(FF)F", (void*)latinime_NativeUtils_powf}
+};
+
+int register_NativeUtils(JNIEnv *env) {
+ const char* const kClassPathName = "com/android/inputmethod/latin/NativeUtils";
+ return registerNativeMethods(env, kClassPathName, sMethods,
+ sizeof(sMethods) / sizeof(sMethods[0]));
+}
+
+} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_latin_NativeUtils.h b/native/jni/com_android_inputmethod_latin_NativeUtils.h
new file mode 100644
index 000000000..13a348a5c
--- /dev/null
+++ b/native/jni/com_android_inputmethod_latin_NativeUtils.h
@@ -0,0 +1,29 @@
+/*
+**
+** 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.
+*/
+
+#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H
+#define _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H
+
+#include "jni.h"
+
+namespace latinime {
+
+int register_NativeUtils(JNIEnv *env);
+
+}
+
+#endif // _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H
diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp
index b9e2c3255..1314bab27 100644
--- a/native/jni/jni_common.cpp
+++ b/native/jni/jni_common.cpp
@@ -19,6 +19,7 @@
#include "com_android_inputmethod_keyboard_ProximityInfo.h"
#include "com_android_inputmethod_latin_BinaryDictionary.h"
+#include "com_android_inputmethod_latin_NativeUtils.h"
#include "defines.h"
#include "jni.h"
#include "proximity_info.h"
@@ -52,6 +53,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
goto bail;
}
+ if (!register_NativeUtils(env)) {
+ AKLOGE("ERROR: NativeUtils native registration failed");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 99f5b92c1..6e7d2c807 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -27,6 +27,7 @@
#include "defines.h"
#include "dictionary.h"
#include "proximity_info.h"
+#include "proximity_info_state.h"
namespace latinime {
@@ -97,7 +98,7 @@ inline static int getCurrentEditDistance(int *editDistanceTable, const int editD
static const char QUOTE = '\'';
inline bool Correction::isQuote(const unsigned short c) {
- const unsigned short userTypedChar = mProximityInfo->getPrimaryCharAt(mInputIndex);
+ const unsigned short userTypedChar = mProximityInfoState.getPrimaryCharAt(mInputIndex);
return (c == QUOTE && userTypedChar != QUOTE);
}
@@ -282,7 +283,7 @@ bool Correction::needsToPrune() const {
void Correction::addCharToCurrentWord(const int32_t c) {
mWord[mOutputIndex] = c;
- const unsigned short *primaryInputWord = mProximityInfo->getPrimaryInputWord();
+ const unsigned short *primaryInputWord = mProximityInfoState.getPrimaryInputWord();
calcEditDistanceOneStep(mEditDistanceTable, primaryInputWord, mInputLength,
mWord, mOutputIndex + 1);
}
@@ -308,13 +309,12 @@ Correction::CorrectionType Correction::processUnrelatedCorrectionType() {
return UNRELATED;
}
-inline bool isEquivalentChar(ProximityInfo::ProximityType type) {
- return type == ProximityInfo::EQUIVALENT_CHAR;
+inline bool isEquivalentChar(ProximityType type) {
+ return type == EQUIVALENT_CHAR;
}
-inline bool isProximityCharOrEquivalentChar(ProximityInfo::ProximityType type) {
- return type == ProximityInfo::EQUIVALENT_CHAR
- || type == ProximityInfo::NEAR_PROXIMITY_CHAR;
+inline bool isProximityCharOrEquivalentChar(ProximityType type) {
+ return type == EQUIVALENT_CHAR || type == NEAR_PROXIMITY_CHAR;
}
Correction::CorrectionType Correction::processCharAndCalcState(
@@ -335,19 +335,19 @@ Correction::CorrectionType Correction::processCharAndCalcState(
bool incremented = false;
if (mLastCharExceeded && mInputIndex == mInputLength - 1) {
// TODO: Do not check the proximity if EditDistance exceeds the threshold
- const ProximityInfo::ProximityType matchId =
- mProximityInfo->getMatchedProximityId(mInputIndex, c, true, &proximityIndex);
+ const ProximityType matchId = mProximityInfoState.getMatchedProximityId(
+ mInputIndex, c, true, &proximityIndex);
if (isEquivalentChar(matchId)) {
mLastCharExceeded = false;
--mExcessiveCount;
mDistances[mOutputIndex] =
- mProximityInfo->getNormalizedSquaredDistance(mInputIndex, 0);
- } else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) {
+ mProximityInfoState.getNormalizedSquaredDistance(mInputIndex, 0);
+ } else if (matchId == NEAR_PROXIMITY_CHAR) {
mLastCharExceeded = false;
--mExcessiveCount;
++mProximityCount;
- mDistances[mOutputIndex] =
- mProximityInfo->getNormalizedSquaredDistance(mInputIndex, proximityIndex);
+ mDistances[mOutputIndex] = mProximityInfoState.getNormalizedSquaredDistance(
+ mInputIndex, proximityIndex);
}
if (!isQuote(c)) {
incrementInputIndex();
@@ -388,7 +388,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
bool secondTransposing = false;
if (mTransposedCount % 2 == 1) {
- if (isEquivalentChar(mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) {
+ if (isEquivalentChar(mProximityInfoState.getMatchedProximityId(
+ mInputIndex - 1, c, false))) {
++mTransposedCount;
secondTransposing = true;
} else if (mCorrectionStates[mOutputIndex].mExceeding) {
@@ -417,17 +418,17 @@ Correction::CorrectionType Correction::processCharAndCalcState(
? (noCorrectionsHappenedSoFar || mProximityCount == 0)
: (noCorrectionsHappenedSoFar && mProximityCount == 0);
- ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
- ? ProximityInfo::EQUIVALENT_CHAR
- : mProximityInfo->getMatchedProximityId(
+ ProximityType matchedProximityCharId = secondTransposing
+ ? EQUIVALENT_CHAR
+ : mProximityInfoState.getMatchedProximityId(
mInputIndex, c, checkProximityChars, &proximityIndex);
- if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId
- || ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
+ if (UNRELATED_CHAR == matchedProximityCharId
+ || ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
if (canTryCorrection && mOutputIndex > 0
&& mCorrectionStates[mOutputIndex].mProximityMatching
&& mCorrectionStates[mOutputIndex].mExceeding
- && isEquivalentChar(mProximityInfo->getMatchedProximityId(
+ && isEquivalentChar(mProximityInfoState.getMatchedProximityId(
mInputIndex, mWord[mOutputIndex - 1], false))) {
if (DEBUG_CORRECTION
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
@@ -446,14 +447,14 @@ Correction::CorrectionType Correction::processCharAndCalcState(
// Here, we are doing something equivalent to matchedProximityCharId,
// but we already know that "excessive char correction" just happened
// so that we just need to check "mProximityCount == 0".
- matchedProximityCharId = mProximityInfo->getMatchedProximityId(
+ matchedProximityCharId = mProximityInfoState.getMatchedProximityId(
mInputIndex, c, mProximityCount == 0, &proximityIndex);
}
}
- if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId
- || ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
- if (ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
+ if (UNRELATED_CHAR == matchedProximityCharId
+ || ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
+ if (ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
mAdditionalProximityMatching = true;
}
// TODO: Optimize
@@ -463,10 +464,10 @@ Correction::CorrectionType Correction::processCharAndCalcState(
if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0
&& !mCorrectionStates[mOutputIndex].mTransposing
&& mCorrectionStates[mOutputIndex - 1].mTransposing
- && isEquivalentChar(mProximityInfo->getMatchedProximityId(
+ && isEquivalentChar(mProximityInfoState.getMatchedProximityId(
mInputIndex, mWord[mOutputIndex - 1], false))
&& isEquivalentChar(
- mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) {
+ mProximityInfoState.getMatchedProximityId(mInputIndex + 1, c, false))) {
// Conversion t->e
// Example:
// occaisional -> occa sional
@@ -478,7 +479,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
&& !mCorrectionStates[mOutputIndex].mTransposing
&& mCorrectionStates[mOutputIndex - 1].mTransposing
&& isEquivalentChar(
- mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) {
+ mProximityInfoState.getMatchedProximityId(mInputIndex - 1, c, false))) {
// Conversion t->s
// Example:
// chcolate -> chocolate
@@ -490,7 +491,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
&& mCorrectionStates[mOutputIndex].mProximityMatching
&& mCorrectionStates[mOutputIndex].mSkipping
&& isEquivalentChar(
- mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) {
+ mProximityInfoState.getMatchedProximityId(mInputIndex - 1, c, false))) {
// Conversion p->s
// Note: This logic tries saving cases like contrst --> contrast -- "a" is one of
// proximity chars of "s", but it should rather be handled as a skipped char.
@@ -502,7 +503,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
&& mCorrectionStates[mOutputIndex].mSkipping
&& mCorrectionStates[mOutputIndex].mAdditionalProximityMatching
&& isProximityCharOrEquivalentChar(
- mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) {
+ mProximityInfoState.getMatchedProximityId(mInputIndex + 1, c, false))) {
// Conversion s->a
incrementInputIndex();
--mSkippedCount;
@@ -511,7 +512,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
mDistances[mOutputIndex] = ADDITIONAL_PROXIMITY_CHAR_DISTANCE_INFO;
} else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputLength
&& isEquivalentChar(
- mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) {
+ mProximityInfoState.getMatchedProximityId(mInputIndex + 1, c, false))) {
// 1.2. Excessive or transpose correction
if (mTransposing) {
++mTransposedCount;
@@ -543,7 +544,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
mTransposedCount, mExcessiveCount, c);
}
return processSkipChar(c, isTerminal, false);
- } else if (ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
+ } else if (ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
// As a last resort, use additional proximity characters
mProximityMatching = true;
++mProximityCount;
@@ -573,12 +574,12 @@ Correction::CorrectionType Correction::processCharAndCalcState(
} else if (isEquivalentChar(matchedProximityCharId)) {
mMatching = true;
++mEquivalentCharCount;
- mDistances[mOutputIndex] = mProximityInfo->getNormalizedSquaredDistance(mInputIndex, 0);
- } else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
+ mDistances[mOutputIndex] = mProximityInfoState.getNormalizedSquaredDistance(mInputIndex, 0);
+ } else if (NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
mProximityMatching = true;
++mProximityCount;
mDistances[mOutputIndex] =
- mProximityInfo->getNormalizedSquaredDistance(mInputIndex, proximityIndex);
+ mProximityInfoState.getNormalizedSquaredDistance(mInputIndex, proximityIndex);
if (DEBUG_CORRECTION
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
@@ -662,7 +663,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
const int excessivePos = correction->getExcessivePos();
const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER;
const int fullWordMultiplier = correction->FULL_WORD_MULTIPLIER;
- const ProximityInfo *proximityInfo = correction->mProximityInfo;
+ const ProximityInfoState *proximityInfoState = &correction->mProximityInfoState;
const int skippedCount = correction->mSkippedCount;
const int transposedCount = correction->mTransposedCount / 2;
const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2;
@@ -685,7 +686,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
const bool skipped = skippedCount > 0;
const int quoteDiffCount = max(0, getQuoteCount(word, outputLength)
- - getQuoteCount(proximityInfo->getPrimaryInputWord(), inputLength));
+ - getQuoteCount(proximityInfoState->getPrimaryInputWord(), inputLength));
// TODO: Calculate edit distance for transposed and excessive
int ed = 0;
@@ -737,8 +738,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
multiplyIntCapped(matchWeight, &finalFreq);
}
- if (proximityInfo->getMatchedProximityId(0, word[0], true)
- == ProximityInfo::UNRELATED_CHAR) {
+ if (proximityInfoState->getMatchedProximityId(0, word[0], true) == UNRELATED_CHAR) {
multiplyRate(FIRST_CHAR_DIFFERENT_DEMOTION_RATE, &finalFreq);
}
@@ -764,7 +764,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
// Demotion for a word with excessive character
if (excessiveCount > 0) {
multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq);
- if (!lastCharExceeded && !proximityInfo->existsAdjacentProximityChars(excessivePos)) {
+ if (!lastCharExceeded && !proximityInfoState->existsAdjacentProximityChars(excessivePos)) {
if (DEBUG_DICT_FULL) {
AKLOGI("Double excessive demotion");
}
@@ -775,8 +775,9 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
}
const bool performTouchPositionCorrection =
- CALIBRATE_SCORE_BY_TOUCH_COORDINATES && proximityInfo->touchPositionCorrectionEnabled()
- && skippedCount == 0 && excessiveCount == 0 && transposedCount == 0;
+ CALIBRATE_SCORE_BY_TOUCH_COORDINATES
+ && proximityInfoState->touchPositionCorrectionEnabled()
+ && skippedCount == 0 && excessiveCount == 0 && transposedCount == 0;
// Score calibration by touch coordinates is being done only for pure-fat finger typing error
// cases.
int additionalProximityCount = 0;
@@ -796,7 +797,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
static const float R1 = NEUTRAL_SCORE_SQUARED_RADIUS;
static const float R2 = HALF_SCORE_SQUARED_RADIUS;
const float x = (float)squaredDistance
- / ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
+ / ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
const float factor = max((x < R1)
? (A * (R1 - x) + B * x) / R1
: (B * (R2 - x) + C * (x - R1)) / (R2 - R1), MIN);
@@ -907,7 +908,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex
if (DEBUG_CORRECTION_FREQ
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == inputLength)) {
- DUMP_WORD(proximityInfo->getPrimaryInputWord(), inputLength);
+ DUMP_WORD(correction->getPrimaryInputWord(), inputLength);
DUMP_WORD(correction->mWord, outputLength);
AKLOGI("FinalFreq: [P%d, S%d, T%d, E%d, A%d] %d, %d, %d, %d, %d, %d", proximityMatchedCount,
skippedCount, transposedCount, excessiveCount, additionalProximityCount,
@@ -1146,5 +1147,4 @@ float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short* be
const float weight = 1.0 - (float) distance / afterLength;
return (score / maxScore) * weight;
}
-
} // namespace latinime
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index 3300a8491..60d7dc33f 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -19,9 +19,10 @@
#include <assert.h>
#include <stdint.h>
-#include "correction_state.h"
+#include "correction_state.h"
#include "defines.h"
+#include "proximity_info_state.h"
namespace latinime {
@@ -178,6 +179,21 @@ class Correction {
static const int FULL_WORD_MULTIPLIER = 2;
};
+ // proximity info state
+ void initInputParams(const ProximityInfo *proximityInfo, const int32_t *inputCodes,
+ const int inputLength, const int *xCoordinates, const int *yCoordinates) {
+ mProximityInfoState.initInputParams(
+ proximityInfo, inputCodes, inputLength, xCoordinates, yCoordinates);
+ }
+
+ const unsigned short* getPrimaryInputWord() const {
+ return mProximityInfoState.getPrimaryInputWord();
+ }
+
+ unsigned short getPrimaryCharAt(const int index) const {
+ return mProximityInfoState.getPrimaryCharAt(index);
+ }
+
private:
inline void incrementInputIndex();
inline void incrementOutputIndex();
@@ -240,7 +256,7 @@ class Correction {
bool mExceeding;
bool mTransposing;
bool mSkipping;
-
+ ProximityInfoState mProximityInfoState;
};
} // namespace latinime
#endif // LATINIME_CORRECTION_H
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index cd2fc634a..e4c6753f4 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -225,6 +225,9 @@ static inline void prof_out(void) {
// This is only used for the size of array. Not to be used in c functions.
#define MAX_WORD_LENGTH_INTERNAL 48
+// This must be the same as ProximityInfo#MAX_PROXIMITY_CHARS_SIZE, currently it's 16.
+#define MAX_PROXIMITY_CHARS_SIZE_INTERNAL 16
+
// This must be equal to ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE in KeyDetector.java
#define ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE 2
@@ -289,4 +292,16 @@ template<typename T> inline T max(T a, T b) { return a > b ? a : b; }
#define INPUTLENGTH_FOR_DEBUG -1
#define MIN_OUTPUT_INDEX_FOR_DEBUG -1
+// Used as a return value for character comparison
+typedef enum {
+ // Same char, possibly with different case or accent
+ EQUIVALENT_CHAR,
+ // It is a char located nearby on the keyboard
+ NEAR_PROXIMITY_CHAR,
+ // It is an unrelated char
+ UNRELATED_CHAR,
+ // Additional proximity char which can differ by language.
+ ADDITIONAL_PROXIMITY_CHAR
+} ProximityType;
+
#endif // LATINIME_DEFINES_H
diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp
index 960d40119..2ba244a7c 100644
--- a/native/jni/src/proximity_info.cpp
+++ b/native/jni/src/proximity_info.cpp
@@ -24,6 +24,7 @@
#include "defines.h"
#include "dictionary.h"
#include "proximity_info.h"
+#include "proximity_info_state.h"
namespace latinime {
@@ -51,23 +52,14 @@ ProximityInfo::ProximityInfo(const std::string localeStr, const int maxProximity
HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
&& keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
&& sweetSpotCenterYs && sweetSpotRadii),
- mLocaleStr(localeStr),
- mInputXCoordinates(0), mInputYCoordinates(0),
- mTouchPositionCorrectionEnabled(false) {
+ mLocaleStr(localeStr) {
const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
- mProximityCharsArray = new int32_t[proximityGridLength];
- mInputCodes = new int32_t[MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH_INTERNAL];
if (DEBUG_PROXIMITY_INFO) {
AKLOGI("Create proximity info array %d", proximityGridLength);
}
+ mProximityCharsArray = new int32_t[proximityGridLength];
memcpy(mProximityCharsArray, proximityCharsArray,
proximityGridLength * sizeof(mProximityCharsArray[0]));
- const int normalizedSquaredDistancesLength =
- MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH_INTERNAL;
- mNormalizedSquaredDistances = new int[normalizedSquaredDistancesLength];
- for (int i = 0; i < normalizedSquaredDistancesLength; ++i) {
- mNormalizedSquaredDistances[i] = NOT_A_DISTANCE;
- }
copyOrFillZero(mKeyXCoordinates, keyXCoordinates, KEY_COUNT * sizeof(mKeyXCoordinates[0]));
copyOrFillZero(mKeyYCoordinates, keyYCoordinates, KEY_COUNT * sizeof(mKeyYCoordinates[0]));
@@ -96,9 +88,7 @@ void ProximityInfo::initializeCodeToKeyIndex() {
}
ProximityInfo::~ProximityInfo() {
- delete[] mNormalizedSquaredDistances;
delete[] mProximityCharsArray;
- delete[] mInputCodes;
}
inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
@@ -119,26 +109,18 @@ bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
if (DEBUG_PROXIMITY_INFO) {
AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
}
+ int32_t* proximityCharsArray = mProximityCharsArray;
for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
if (DEBUG_PROXIMITY_INFO) {
AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
}
- if (mProximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
+ if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
return true;
}
}
return false;
}
-bool ProximityInfo::isOnKey(const int keyId, const int x, const int y) const {
- if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
- const int left = mKeyXCoordinates[keyId];
- const int top = mKeyYCoordinates[keyId];
- const int right = left + mKeyWidths[keyId] + 1;
- const int bottom = top + mKeyHeights[keyId];
- return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom;
-}
-
int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
const int left = mKeyXCoordinates[keyId];
@@ -154,12 +136,13 @@ int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int
void ProximityInfo::calculateNearbyKeyCodes(
const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
+ int32_t *proximityCharsArray = mProximityCharsArray;
int insertPos = 0;
inputCodes[insertPos++] = primaryKey;
const int startIndex = getStartIndexFromCoordinates(x, y);
if (startIndex >= 0) {
for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
- const int32_t c = mProximityCharsArray[startIndex + i];
+ const int32_t c = proximityCharsArray[startIndex + i];
if (c < KEYCODE_SPACE || c == primaryKey) {
continue;
}
@@ -216,115 +199,6 @@ void ProximityInfo::calculateNearbyKeyCodes(
}
}
-void ProximityInfo::setInputParams(const int32_t* inputCodes, const int inputLength,
- const int* xCoordinates, const int* yCoordinates) {
- memset(mInputCodes, 0,
- MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE * sizeof(mInputCodes[0]));
-
- for (int i = 0; i < inputLength; ++i) {
- const int32_t primaryKey = inputCodes[i];
- const int x = xCoordinates[i];
- const int y = yCoordinates[i];
- int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE];
- calculateNearbyKeyCodes(x, y, primaryKey, proximities);
- }
-
- if (DEBUG_PROXIMITY_CHARS) {
- for (int i = 0; i < inputLength; ++i) {
- AKLOGI("---");
- for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; ++j) {
- int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE + j];
- int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE + j];
- icc+= 0;
- icfjc += 0;
- AKLOGI("--- (%d)%c,%c", i, icc, icfjc);
- AKLOGI("--- A<%d>,B<%d>", icc, icfjc);
- }
- }
- }
- //Keep for debug, sorry
- //for (int i = 0; i < MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE; ++i) {
- //if (i < inputLength * MAX_PROXIMITY_CHARS_SIZE) {
- //mInputCodes[i] = mInputCodesFromJava[i];
- //} else {
- // mInputCodes[i] = 0;
- // }
- //}
- mInputXCoordinates = xCoordinates;
- mInputYCoordinates = yCoordinates;
- mTouchPositionCorrectionEnabled =
- HAS_TOUCH_POSITION_CORRECTION_DATA && xCoordinates && yCoordinates;
- mInputLength = inputLength;
- for (int i = 0; i < inputLength; ++i) {
- mPrimaryInputWord[i] = getPrimaryCharAt(i);
- }
- mPrimaryInputWord[inputLength] = 0;
- if (DEBUG_PROXIMITY_CHARS) {
- AKLOGI("--- setInputParams");
- }
- for (int i = 0; i < mInputLength; ++i) {
- const int *proximityChars = getProximityCharsAt(i);
- const int primaryKey = proximityChars[0];
- const int x = xCoordinates[i];
- const int y = yCoordinates[i];
- if (DEBUG_PROXIMITY_CHARS) {
- int a = x + y + primaryKey;
- a += 0;
- AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y);
- // Keep debug code just in case
- //int proximities[50];
- //for (int m = 0; m < 50; ++m) {
- //proximities[m] = 0;
- //}
- //calculateNearbyKeyCodes(x, y, primaryKey, proximities);
- //for (int l = 0; l < 50 && proximities[l] > 0; ++l) {
- //if (DEBUG_PROXIMITY_CHARS) {
- //AKLOGI("--- native Proximity (%d) = %c", l, proximities[l]);
- //}
- //}
- }
- for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE && proximityChars[j] > 0; ++j) {
- const int currentChar = proximityChars[j];
- const float squaredDistance = hasInputCoordinates()
- ? calculateNormalizedSquaredDistance(getKeyIndex(currentChar), i)
- : NOT_A_DISTANCE_FLOAT;
- if (squaredDistance >= 0.0f) {
- mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] =
- (int)(squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
- } else {
- mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] = (j == 0)
- ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO
- : PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
- }
- if (DEBUG_PROXIMITY_CHARS) {
- AKLOGI("--- Proximity (%d) = %c", j, currentChar);
- }
- }
- }
-}
-
-inline float square(const float x) { return x * x; }
-
-float ProximityInfo::calculateNormalizedSquaredDistance(
- const int keyIndex, const int inputIndex) const {
- if (keyIndex == NOT_AN_INDEX) {
- return NOT_A_DISTANCE_FLOAT;
- }
- if (!hasSweetSpotData(keyIndex)) {
- return NOT_A_DISTANCE_FLOAT;
- }
- if (NOT_A_COORDINATE == mInputXCoordinates[inputIndex]) {
- return NOT_A_DISTANCE_FLOAT;
- }
- const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(keyIndex, inputIndex);
- const float squaredRadius = square(mSweetSpotRadii[keyIndex]);
- return squaredDistance / squaredRadius;
-}
-
-bool ProximityInfo::hasInputCoordinates() const {
- return mInputXCoordinates && mInputYCoordinates;
-}
-
int ProximityInfo::getKeyIndex(const int c) const {
if (KEY_COUNT == 0) {
// We do not have the coordinate data
@@ -336,132 +210,4 @@ int ProximityInfo::getKeyIndex(const int c) const {
}
return mCodeToKeyIndex[baseLowerC];
}
-
-float ProximityInfo::calculateSquaredDistanceFromSweetSpotCenter(
- const int keyIndex, const int inputIndex) const {
- const float sweetSpotCenterX = mSweetSpotCenterXs[keyIndex];
- const float sweetSpotCenterY = mSweetSpotCenterYs[keyIndex];
- const float inputX = (float)mInputXCoordinates[inputIndex];
- const float inputY = (float)mInputYCoordinates[inputIndex];
- return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY);
-}
-
-inline const int* ProximityInfo::getProximityCharsAt(const int index) const {
- return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE);
-}
-
-unsigned short ProximityInfo::getPrimaryCharAt(const int index) const {
- return getProximityCharsAt(index)[0];
-}
-
-inline bool ProximityInfo::existsCharInProximityAt(const int index, const int c) const {
- const int *chars = getProximityCharsAt(index);
- int i = 0;
- while (chars[i] > 0 && i < MAX_PROXIMITY_CHARS_SIZE) {
- if (chars[i++] == c) {
- return true;
- }
- }
- return false;
-}
-
-bool ProximityInfo::existsAdjacentProximityChars(const int index) const {
- if (index < 0 || index >= mInputLength) return false;
- const int currentChar = getPrimaryCharAt(index);
- const int leftIndex = index - 1;
- if (leftIndex >= 0 && existsCharInProximityAt(leftIndex, currentChar)) {
- return true;
- }
- const int rightIndex = index + 1;
- if (rightIndex < mInputLength && existsCharInProximityAt(rightIndex, currentChar)) {
- return true;
- }
- return false;
-}
-
-// In the following function, c is the current character of the dictionary word
-// currently examined.
-// currentChars is an array containing the keys close to the character the
-// user actually typed at the same position. We want to see if c is in it: if so,
-// then the word contains at that position a character close to what the user
-// typed.
-// What the user typed is actually the first character of the array.
-// proximityIndex is a pointer to the variable where getMatchedProximityId returns
-// the index of c in the proximity chars of the input index.
-// Notice : accented characters do not have a proximity list, so they are alone
-// in their list. The non-accented version of the character should be considered
-// "close", but not the other keys close to the non-accented version.
-ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(const int index,
- const unsigned short c, const bool checkProximityChars, int *proximityIndex) const {
- const int *currentChars = getProximityCharsAt(index);
- const int firstChar = currentChars[0];
- const unsigned short baseLowerC = toBaseLowerCase(c);
-
- // The first char in the array is what user typed. If it matches right away,
- // that means the user typed that same char for this pos.
- if (firstChar == baseLowerC || firstChar == c) {
- return EQUIVALENT_CHAR;
- }
-
- if (!checkProximityChars) return UNRELATED_CHAR;
-
- // If the non-accented, lowercased version of that first character matches c,
- // then we have a non-accented version of the accented character the user
- // typed. Treat it as a close char.
- if (toBaseLowerCase(firstChar) == baseLowerC)
- return NEAR_PROXIMITY_CHAR;
-
- // Not an exact nor an accent-alike match: search the list of close keys
- int j = 1;
- while (j < MAX_PROXIMITY_CHARS_SIZE
- && currentChars[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
- const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
- if (matched) {
- if (proximityIndex) {
- *proximityIndex = j;
- }
- return NEAR_PROXIMITY_CHAR;
- }
- ++j;
- }
- if (j < MAX_PROXIMITY_CHARS_SIZE
- && currentChars[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
- ++j;
- while (j < MAX_PROXIMITY_CHARS_SIZE
- && currentChars[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
- const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
- if (matched) {
- if (proximityIndex) {
- *proximityIndex = j;
- }
- return ADDITIONAL_PROXIMITY_CHAR;
- }
- ++j;
- }
- }
-
- // Was not included, signal this as an unrelated character.
- return UNRELATED_CHAR;
-}
-
-bool ProximityInfo::sameAsTyped(const unsigned short *word, int length) const {
- if (length != mInputLength) {
- return false;
- }
- const int *inputCodes = mInputCodes;
- while (length--) {
- if ((unsigned int) *inputCodes != (unsigned int) *word) {
- return false;
- }
- inputCodes += MAX_PROXIMITY_CHARS_SIZE;
- word++;
- }
- return true;
-}
-
-const int ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
-const int ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
-const int ProximityInfo::MAX_KEY_COUNT_IN_A_KEYBOARD;
-const int ProximityInfo::MAX_CHAR_CODE;
-
} // namespace latinime
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index feb0c9444..67f2f60fb 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -28,22 +28,6 @@ class Correction;
class ProximityInfo {
public:
- static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
- static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
- 1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
-
- // Used as a return value for character comparison
- typedef enum {
- // Same char, possibly with different case or accent
- EQUIVALENT_CHAR,
- // It is a char located nearby on the keyboard
- NEAR_PROXIMITY_CHAR,
- // It is an unrelated char
- UNRELATED_CHAR,
- // Additional proximity char which can differ by language.
- ADDITIONAL_PROXIMITY_CHAR
- } ProximityType;
-
ProximityInfo(const std::string localeStr, const int maxProximityCharsSize,
const int keyboardWidth, const int keyboardHeight, const int gridWidth,
const int gridHeight, const int mostCommonkeyWidth,
@@ -53,23 +37,65 @@ class ProximityInfo {
const float *sweetSpotCenterYs, const float *sweetSpotRadii);
~ProximityInfo();
bool hasSpaceProximity(const int x, const int y) const;
- void setInputParams(const int32_t *inputCodes, const int inputLength,
- const int *xCoordinates, const int *yCoordinates);
- const int* getProximityCharsAt(const int index) const;
- unsigned short getPrimaryCharAt(const int index) const;
- bool existsCharInProximityAt(const int index, const int c) const;
- bool existsAdjacentProximityChars(const int index) const;
- ProximityType getMatchedProximityId(const int index, const unsigned short c,
- const bool checkProximityChars, int *proximityIndex = 0) const;
- int getNormalizedSquaredDistance(const int inputIndex, const int proximityIndex) const {
- return mNormalizedSquaredDistances[inputIndex * MAX_PROXIMITY_CHARS_SIZE + proximityIndex];
- }
+ int getNormalizedSquaredDistance(const int inputIndex, const int proximityIndex) const;
bool sameAsTyped(const unsigned short *word, int length) const;
- const unsigned short* getPrimaryInputWord() const {
- return mPrimaryInputWord;
+ int squaredDistanceToEdge(const int keyId, const int x, const int y) const;
+ bool isOnKey(const int keyId, const int x, const int y) const {
+ if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
+ const int left = mKeyXCoordinates[keyId];
+ const int top = mKeyYCoordinates[keyId];
+ const int right = left + mKeyWidths[keyId] + 1;
+ const int bottom = top + mKeyHeights[keyId];
+ return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom;
+ }
+ int getKeyIndex(const int c) const;
+ bool hasSweetSpotData(const int keyIndex) const {
+ // When there are no calibration data for a key,
+ // the radius of the key is assigned to zero.
+ return mSweetSpotRadii[keyIndex] > 0.0;
+ }
+ float getSweetSpotRadiiAt(int keyIndex) const {
+ return mSweetSpotRadii[keyIndex];
+ }
+ float getSweetSpotCenterXAt(int keyIndex) const {
+ return mSweetSpotCenterXs[keyIndex];
+ }
+ float getSweetSpotCenterYAt(int keyIndex) const {
+ return mSweetSpotCenterYs[keyIndex];
+ }
+ void calculateNearbyKeyCodes(
+ const int x, const int y, const int32_t primaryKey, int *inputCodes) const;
+
+ bool hasTouchPositionCorrectionData() const {
+ return HAS_TOUCH_POSITION_CORRECTION_DATA;
+ }
+
+ int getMostCommonKeyWidthSquare() const {
+ return MOST_COMMON_KEY_WIDTH_SQUARE;
+ }
+
+ std::string getLocaleStr() const {
+ return mLocaleStr;
+ }
+
+ int getKeyCount() const {
+ return KEY_COUNT;
+ }
+
+ int getCellHeight() const {
+ return CELL_HEIGHT;
}
- bool touchPositionCorrectionEnabled() const {
- return mTouchPositionCorrectionEnabled;
+
+ int getCellWidth() const {
+ return CELL_WIDTH;
+ }
+
+ int getGridWidth() const {
+ return GRID_WIDTH;
+ }
+
+ int getGridHeight() const {
+ return GRID_HEIGHT;
}
private:
@@ -86,16 +112,6 @@ class ProximityInfo {
float calculateSquaredDistanceFromSweetSpotCenter(
const int keyIndex, const int inputIndex) const;
bool hasInputCoordinates() const;
- int getKeyIndex(const int c) const;
- bool hasSweetSpotData(const int keyIndex) const {
- // When there are no calibration data for a key,
- // the radius of the key is assigned to zero.
- return mSweetSpotRadii[keyIndex] > 0.0;
- }
- bool isOnKey(const int keyId, const int x, const int y) const;
- int squaredDistanceToEdge(const int keyId, const int x, const int y) const;
- void calculateNearbyKeyCodes(
- const int x, const int y, const int32_t primaryKey, int *inputCodes) const;
const int MAX_PROXIMITY_CHARS_SIZE;
const int KEYBOARD_WIDTH;
@@ -108,14 +124,7 @@ class ProximityInfo {
const int KEY_COUNT;
const bool HAS_TOUCH_POSITION_CORRECTION_DATA;
const std::string mLocaleStr;
- // TODO: remove this
- const int *mInputCodesFromJava;
- int32_t *mInputCodes;
- const int *mInputXCoordinates;
- const int *mInputYCoordinates;
- bool mTouchPositionCorrectionEnabled;
int32_t *mProximityCharsArray;
- int *mNormalizedSquaredDistances;
int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
int32_t mKeyWidths[MAX_KEY_COUNT_IN_A_KEYBOARD];
@@ -124,9 +133,8 @@ class ProximityInfo {
float mSweetSpotCenterXs[MAX_KEY_COUNT_IN_A_KEYBOARD];
float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD];
float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD];
- int mInputLength;
- unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL];
int mCodeToKeyIndex[MAX_CHAR_CODE + 1];
+ // TODO: move to correction.h
};
} // namespace latinime
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
new file mode 100644
index 000000000..149299eb6
--- /dev/null
+++ b/native/jni/src/proximity_info_state.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <string>
+
+#define LOG_TAG "LatinIME: proximity_info_state.cpp"
+
+#include "additional_proximity_chars.h"
+#include "defines.h"
+#include "dictionary.h"
+#include "proximity_info.h"
+#include "proximity_info_state.h"
+
+namespace latinime {
+void ProximityInfoState::initInputParams(
+ const ProximityInfo* proximityInfo, const int32_t* inputCodes, const int inputLength,
+ const int* xCoordinates, const int* yCoordinates) {
+ mProximityInfo = proximityInfo;
+ mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
+ mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
+ mLocaleStr = proximityInfo->getLocaleStr();
+ mKeyCount = proximityInfo->getKeyCount();
+ mCellHeight = proximityInfo->getCellHeight();
+ mCellWidth = proximityInfo->getCellWidth();
+ mGridHeight = proximityInfo->getGridWidth();
+ mGridWidth = proximityInfo->getGridHeight();
+ const int normalizedSquaredDistancesLength =
+ MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL;
+ for (int i = 0; i < normalizedSquaredDistancesLength; ++i) {
+ mNormalizedSquaredDistances[i] = NOT_A_DISTANCE;
+ }
+
+ memset(mInputCodes, 0,
+ MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE_INTERNAL * sizeof(mInputCodes[0]));
+
+ for (int i = 0; i < inputLength; ++i) {
+ const int32_t primaryKey = inputCodes[i];
+ const int x = xCoordinates[i];
+ const int y = yCoordinates[i];
+ int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL];
+ mProximityInfo->calculateNearbyKeyCodes(x, y, primaryKey, proximities);
+ }
+
+ if (DEBUG_PROXIMITY_CHARS) {
+ for (int i = 0; i < inputLength; ++i) {
+ AKLOGI("---");
+ for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) {
+ int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
+ int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
+ icc += 0;
+ icfjc += 0;
+ AKLOGI("--- (%d)%c,%c", i, icc, icfjc); AKLOGI("--- A<%d>,B<%d>", icc, icfjc);
+ }
+ }
+ }
+ mInputXCoordinates = xCoordinates;
+ mInputYCoordinates = yCoordinates;
+ mTouchPositionCorrectionEnabled =
+ mHasTouchPositionCorrectionData && xCoordinates && yCoordinates;
+ mInputLength = inputLength;
+ for (int i = 0; i < inputLength; ++i) {
+ mPrimaryInputWord[i] = getPrimaryCharAt(i);
+ }
+ mPrimaryInputWord[inputLength] = 0;
+ if (DEBUG_PROXIMITY_CHARS) {
+ AKLOGI("--- initInputParams");
+ }
+ for (int i = 0; i < mInputLength; ++i) {
+ const int *proximityChars = getProximityCharsAt(i);
+ const int primaryKey = proximityChars[0];
+ const int x = xCoordinates[i];
+ const int y = yCoordinates[i];
+ if (DEBUG_PROXIMITY_CHARS) {
+ int a = x + y + primaryKey;
+ a += 0;
+ AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y);
+ }
+ for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityChars[j] > 0; ++j) {
+ const int currentChar = proximityChars[j];
+ const float squaredDistance =
+ hasInputCoordinates() ? calculateNormalizedSquaredDistance(
+ mProximityInfo->getKeyIndex(currentChar), i) :
+ NOT_A_DISTANCE_FLOAT;
+ if (squaredDistance >= 0.0f) {
+ mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
+ (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
+ } else {
+ mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
+ (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO :
+ PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
+ }
+ if (DEBUG_PROXIMITY_CHARS) {
+ AKLOGI("--- Proximity (%d) = %c", j, currentChar);
+ }
+ }
+ }
+}
+
+float ProximityInfoState::calculateNormalizedSquaredDistance(
+ const int keyIndex, const int inputIndex) const {
+ if (keyIndex == NOT_AN_INDEX) {
+ return NOT_A_DISTANCE_FLOAT;
+ }
+ if (!mProximityInfo->hasSweetSpotData(keyIndex)) {
+ return NOT_A_DISTANCE_FLOAT;
+ }
+ if (NOT_A_COORDINATE == mInputXCoordinates[inputIndex]) {
+ return NOT_A_DISTANCE_FLOAT;
+ }
+ const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(
+ keyIndex, inputIndex);
+ const float squaredRadius = square(mProximityInfo->getSweetSpotRadiiAt(keyIndex));
+ return squaredDistance / squaredRadius;
+}
+
+float ProximityInfoState::calculateSquaredDistanceFromSweetSpotCenter(
+ const int keyIndex, const int inputIndex) const {
+ const float sweetSpotCenterX = mProximityInfo->getSweetSpotCenterXAt(keyIndex);
+ const float sweetSpotCenterY = mProximityInfo->getSweetSpotCenterYAt(keyIndex);
+ const float inputX = (float)mInputXCoordinates[inputIndex];
+ const float inputY = (float)mInputYCoordinates[inputIndex];
+ return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY);
+}
+} // namespace latinime
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
new file mode 100644
index 000000000..3a98d9b6a
--- /dev/null
+++ b/native/jni/src/proximity_info_state.h
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_PROXIMITY_INFO_STATE_H
+#define LATINIME_PROXIMITY_INFO_STATE_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <string>
+
+#include "additional_proximity_chars.h"
+#include "char_utils.h"
+#include "defines.h"
+
+namespace latinime {
+
+class ProximityInfo;
+
+class ProximityInfoState {
+ public:
+ static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
+ static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
+ 1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
+ // The upper limit of the char code in mCodeToKeyIndex
+ static const int MAX_CHAR_CODE = 127;
+ static const float NOT_A_DISTANCE_FLOAT = -1.0f;
+ static const int NOT_A_CODE = -1;
+
+ /////////////////////////////////////////
+ // Defined in proximity_info_state.cpp //
+ /////////////////////////////////////////
+ void initInputParams(
+ const ProximityInfo* proximityInfo, const int32_t* inputCodes, const int inputLength,
+ const int* xCoordinates, const int* yCoordinates);
+
+ /////////////////////////////////////////
+ // Defined here //
+ /////////////////////////////////////////
+ inline const int* getProximityCharsAt(const int index) const {
+ return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL);
+ }
+
+ inline unsigned short getPrimaryCharAt(const int index) const {
+ return getProximityCharsAt(index)[0];
+ }
+
+ inline bool existsCharInProximityAt(const int index, const int c) const {
+ const int *chars = getProximityCharsAt(index);
+ int i = 0;
+ while (chars[i] > 0 && i < MAX_PROXIMITY_CHARS_SIZE_INTERNAL) {
+ if (chars[i++] == c) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline bool existsAdjacentProximityChars(const int index) const {
+ if (index < 0 || index >= mInputLength) return false;
+ const int currentChar = getPrimaryCharAt(index);
+ const int leftIndex = index - 1;
+ if (leftIndex >= 0 && existsCharInProximityAt(leftIndex, currentChar)) {
+ return true;
+ }
+ const int rightIndex = index + 1;
+ if (rightIndex < mInputLength && existsCharInProximityAt(rightIndex, currentChar)) {
+ return true;
+ }
+ return false;
+ }
+
+ // In the following function, c is the current character of the dictionary word
+ // currently examined.
+ // currentChars is an array containing the keys close to the character the
+ // user actually typed at the same position. We want to see if c is in it: if so,
+ // then the word contains at that position a character close to what the user
+ // typed.
+ // What the user typed is actually the first character of the array.
+ // proximityIndex is a pointer to the variable where getMatchedProximityId returns
+ // the index of c in the proximity chars of the input index.
+ // Notice : accented characters do not have a proximity list, so they are alone
+ // in their list. The non-accented version of the character should be considered
+ // "close", but not the other keys close to the non-accented version.
+ inline ProximityType getMatchedProximityId(const int index,
+ const unsigned short c, const bool checkProximityChars, int *proximityIndex = 0) const {
+ const int *currentChars = getProximityCharsAt(index);
+ const int firstChar = currentChars[0];
+ const unsigned short baseLowerC = toBaseLowerCase(c);
+
+ // The first char in the array is what user typed. If it matches right away,
+ // that means the user typed that same char for this pos.
+ if (firstChar == baseLowerC || firstChar == c) {
+ return EQUIVALENT_CHAR;
+ }
+
+ if (!checkProximityChars) return UNRELATED_CHAR;
+
+ // If the non-accented, lowercased version of that first character matches c,
+ // then we have a non-accented version of the accented character the user
+ // typed. Treat it as a close char.
+ if (toBaseLowerCase(firstChar) == baseLowerC)
+ return NEAR_PROXIMITY_CHAR;
+
+ // Not an exact nor an accent-alike match: search the list of close keys
+ int j = 1;
+ while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ && currentChars[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
+ const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
+ if (matched) {
+ if (proximityIndex) {
+ *proximityIndex = j;
+ }
+ return NEAR_PROXIMITY_CHAR;
+ }
+ ++j;
+ }
+ if (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ && currentChars[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
+ ++j;
+ while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ && currentChars[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
+ const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
+ if (matched) {
+ if (proximityIndex) {
+ *proximityIndex = j;
+ }
+ return ADDITIONAL_PROXIMITY_CHAR;
+ }
+ ++j;
+ }
+ }
+
+ // Was not included, signal this as an unrelated character.
+ return UNRELATED_CHAR;
+ }
+
+ inline int getNormalizedSquaredDistance(
+ const int inputIndex, const int proximityIndex) const {
+ return mNormalizedSquaredDistances[
+ inputIndex * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + proximityIndex];
+ }
+
+ inline const unsigned short* getPrimaryInputWord() const {
+ return mPrimaryInputWord;
+ }
+
+ inline bool touchPositionCorrectionEnabled() const {
+ return mTouchPositionCorrectionEnabled;
+ }
+
+ private:
+ /////////////////////////////////////////
+ // Defined in proximity_info_state.cpp //
+ /////////////////////////////////////////
+ float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
+
+ float calculateSquaredDistanceFromSweetSpotCenter(
+ const int keyIndex, const int inputIndex) const;
+
+ /////////////////////////////////////////
+ // Defined here //
+ /////////////////////////////////////////
+ inline float square(const float x) const { return x * x; }
+
+ bool hasInputCoordinates() const {
+ return mInputXCoordinates && mInputYCoordinates;
+ }
+
+ bool sameAsTyped(const unsigned short *word, int length) const {
+ if (length != mInputLength) {
+ return false;
+ }
+ const int *inputCodes = mInputCodes;
+ while (length--) {
+ if ((unsigned int) *inputCodes != (unsigned int) *word) {
+ return false;
+ }
+ inputCodes += MAX_PROXIMITY_CHARS_SIZE_INTERNAL;
+ word++;
+ }
+ return true;
+ }
+
+ // const
+ const ProximityInfo *mProximityInfo;
+ bool mHasTouchPositionCorrectionData;
+ int mMostCommonKeyWidthSquare;
+ std::string mLocaleStr;
+ int mKeyCount;
+ int mCellHeight;
+ int mCellWidth;
+ int mGridHeight;
+ int mGridWidth;
+
+ const int *mInputXCoordinates;
+ const int *mInputYCoordinates;
+ bool mTouchPositionCorrectionEnabled;
+ int32_t mInputCodes[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
+ int mNormalizedSquaredDistances[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
+ int mInputLength;
+ unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL];
+};
+
+} // namespace latinime
+
+#endif // LATINIME_PROXIMITY_INFO_STATE_H
diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp
index ea9f11b2c..27196f493 100644
--- a/native/jni/src/unigram_dictionary.cpp
+++ b/native/jni/src/unigram_dictionary.cpp
@@ -103,7 +103,7 @@ void UnigramDictionary::getWordWithDigraphSuggestionsRec(ProximityInfo *proximit
const bool useFullEditDistance, const int *codesSrc,
const int codesRemain, const int currentDepth, int *codesDest, Correction *correction,
WordsPriorityQueuePool *queuePool,
- const digraph_t* const digraphs, const unsigned int digraphsSize) {
+ const digraph_t* const digraphs, const unsigned int digraphsSize) const {
const int startIndex = codesDest - codesBuffer;
if (currentDepth < MAX_DIGRAPH_SEARCH_DEPTH) {
@@ -173,7 +173,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo,
WordsPriorityQueuePool *queuePool, Correction *correction, const int *xcoordinates,
const int *ycoordinates, const int *codes, const int codesSize,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
- const bool useFullEditDistance, unsigned short *outWords, int *frequencies) {
+ const bool useFullEditDistance, unsigned short *outWords, int *frequencies) const {
queuePool->clearAll();
Correction* masterCorrection = correction;
@@ -205,17 +205,17 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo,
PROF_START(20);
if (DEBUG_DICT) {
float ns = queuePool->getMasterQueue()->getHighestNormalizedScore(
- proximityInfo->getPrimaryInputWord(), codesSize, 0, 0, 0);
+ correction->getPrimaryInputWord(), codesSize, 0, 0, 0);
ns += 0;
AKLOGI("Max normalized score = %f", ns);
}
const int suggestedWordsCount =
queuePool->getMasterQueue()->outputSuggestions(
- proximityInfo->getPrimaryInputWord(), codesSize, frequencies, outWords);
+ correction->getPrimaryInputWord(), codesSize, frequencies, outWords);
if (DEBUG_DICT) {
float ns = queuePool->getMasterQueue()->getHighestNormalizedScore(
- proximityInfo->getPrimaryInputWord(), codesSize, 0, 0, 0);
+ correction->getPrimaryInputWord(), codesSize, 0, 0, 0);
ns += 0;
AKLOGI("Returning %d words", suggestedWordsCount);
/// Print the returned words
@@ -235,7 +235,8 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo,
void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const int inputLength, const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
- const bool useFullEditDistance, Correction *correction, WordsPriorityQueuePool *queuePool) {
+ const bool useFullEditDistance, Correction *correction,
+ WordsPriorityQueuePool *queuePool) const {
PROF_OPEN;
PROF_START(0);
@@ -259,7 +260,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
WordsPriorityQueue* masterQueue = queuePool->getMasterQueue();
if (masterQueue->size() > 0) {
float nsForMaster = masterQueue->getHighestNormalizedScore(
- proximityInfo->getPrimaryInputWord(), inputLength, 0, 0, 0);
+ correction->getPrimaryInputWord(), inputLength, 0, 0, 0);
hasAutoCorrectionCandidate = (nsForMaster > START_TWO_WORDS_CORRECTION_THRESHOLD);
}
PROF_END(4);
@@ -288,11 +289,11 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
const unsigned short* word = sw->mWord;
const int wordLength = sw->mWordLength;
float ns = Correction::RankingAlgorithm::calcNormalizedScore(
- proximityInfo->getPrimaryInputWord(), i, word, wordLength, score);
+ correction->getPrimaryInputWord(), i, word, wordLength, score);
ns += 0;
AKLOGI("--- TOP SUB WORDS for %d --- %d %f [%d]", i, score, ns,
(ns > TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD));
- DUMP_WORD(proximityInfo->getPrimaryInputWord(), i);
+ DUMP_WORD(correction->getPrimaryInputWord(), i);
DUMP_WORD(word, wordLength);
}
}
@@ -300,12 +301,13 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
}
void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xCoordinates,
- const int *yCoordinates, const int *codes, const int inputLength, Correction *correction) {
+ const int *yCoordinates, const int *codes, const int inputLength,
+ Correction *correction) const {
if (DEBUG_DICT) {
AKLOGI("initSuggest");
DUMP_WORD_INT(codes, inputLength);
}
- proximityInfo->setInputParams(codes, inputLength, xCoordinates, yCoordinates);
+ correction->initInputParams(proximityInfo, codes, inputLength, xCoordinates, yCoordinates);
const int maxDepth = min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
correction->initCorrection(proximityInfo, inputLength, maxDepth);
}
@@ -317,7 +319,7 @@ void UnigramDictionary::getOneWordSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
const bool useFullEditDistance, const int inputLength,
- Correction *correction, WordsPriorityQueuePool *queuePool) {
+ Correction *correction, WordsPriorityQueuePool *queuePool) const {
initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, inputLength, correction);
getSuggestionCandidates(useFullEditDistance, inputLength, bigramMap, bigramFilter, correction,
queuePool, true /* doAutoCompletion */, DEFAULT_MAX_ERRORS, FIRST_WORD_INDEX);
@@ -326,7 +328,7 @@ void UnigramDictionary::getOneWordSuggestions(ProximityInfo *proximityInfo,
void UnigramDictionary::getSuggestionCandidates(const bool useFullEditDistance,
const int inputLength, const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
Correction *correction, WordsPriorityQueuePool *queuePool,
- const bool doAutoCompletion, const int maxErrors, const int currentWordIndex) {
+ const bool doAutoCompletion, const int maxErrors, const int currentWordIndex) const {
uint8_t totalTraverseCount = correction->pushAndGetTotalTraverseCount();
if (DEBUG_DICT) {
AKLOGI("Traverse count %d", totalTraverseCount);
@@ -374,7 +376,7 @@ void UnigramDictionary::getSuggestionCandidates(const bool useFullEditDistance,
inline void UnigramDictionary::onTerminal(const int probability,
const TerminalAttributes& terminalAttributes, Correction *correction,
WordsPriorityQueuePool *queuePool, const bool addToMasterQueue,
- const int currentWordIndex) {
+ const int currentWordIndex) const {
const int inputIndex = correction->getInputIndex();
const bool addToSubQueue = inputIndex < SUB_QUEUE_MAX_COUNT;
@@ -430,7 +432,7 @@ int UnigramDictionary::getSubStringSuggestion(
const bool hasAutoCorrectionCandidate, const int currentWordIndex,
const int inputWordStartPos, const int inputWordLength,
const int outputWordStartPos, const bool isSpaceProximity, int *freqArray,
- int*wordLengthArray, unsigned short* outputWord, int *outputWordLength) {
+ int*wordLengthArray, unsigned short* outputWord, int *outputWordLength) const {
if (inputWordLength > MULTIPLE_WORDS_SUGGESTION_MAX_WORD_LENGTH) {
return FLAG_MULTIPLE_SUGGEST_ABORT;
}
@@ -479,11 +481,12 @@ int UnigramDictionary::getSubStringSuggestion(
initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
inputLength, correction);
+ unsigned short word[MAX_WORD_LENGTH_INTERNAL];
int freq = getMostFrequentWordLike(
- inputWordStartPos, inputWordLength, proximityInfo, mWord);
+ inputWordStartPos, inputWordLength, correction, word);
if (freq > 0) {
nextWordLength = inputWordLength;
- tempOutputWord = mWord;
+ tempOutputWord = word;
} else if (!hasAutoCorrectionCandidate) {
if (inputWordStartPos > 0) {
const int offset = inputWordStartPos;
@@ -510,7 +513,7 @@ int UnigramDictionary::getSubStringSuggestion(
}
int score = 0;
const float ns = queue->getHighestNormalizedScore(
- proximityInfo->getPrimaryInputWord(), inputWordLength,
+ correction->getPrimaryInputWord(), inputWordLength,
&tempOutputWord, &score, &nextWordLength);
if (DEBUG_DICT) {
AKLOGI("NS(%d) = %f, Score = %d", currentWordIndex, ns, score);
@@ -577,7 +580,7 @@ void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo,
Correction *correction, WordsPriorityQueuePool* queuePool,
const bool hasAutoCorrectionCandidate, const int startInputPos, const int startWordIndex,
const int outputWordLength, int *freqArray, int* wordLengthArray,
- unsigned short* outputWord) {
+ unsigned short* outputWord) const {
if (startWordIndex >= (MULTIPLE_WORDS_SUGGESTION_MAX_WORDS - 1)) {
// Return if the last word index
return;
@@ -656,7 +659,7 @@ void UnigramDictionary::getSplitMultipleWordsSuggestions(ProximityInfo *proximit
const int *xcoordinates, const int *ycoordinates, const int *codes,
const bool useFullEditDistance, const int inputLength,
Correction *correction, WordsPriorityQueuePool* queuePool,
- const bool hasAutoCorrectionCandidate) {
+ const bool hasAutoCorrectionCandidate) const {
if (inputLength >= MAX_WORD_LENGTH) return;
if (DEBUG_DICT) {
AKLOGI("--- Suggest multiple words");
@@ -678,11 +681,11 @@ void UnigramDictionary::getSplitMultipleWordsSuggestions(ProximityInfo *proximit
// Wrapper for getMostFrequentWordLikeInner, which matches it to the previous
// interface.
inline int UnigramDictionary::getMostFrequentWordLike(const int startInputIndex,
- const int inputLength, ProximityInfo *proximityInfo, unsigned short *word) {
+ const int inputLength, Correction *correction, unsigned short *word) const {
uint16_t inWord[inputLength];
for (int i = 0; i < inputLength; ++i) {
- inWord[i] = (uint16_t)proximityInfo->getPrimaryCharAt(startInputIndex + i);
+ inWord[i] = (uint16_t)correction->getPrimaryCharAt(startInputIndex + i);
}
return getMostFrequentWordLikeInner(inWord, inputLength, word);
}
@@ -751,21 +754,24 @@ static inline void onTerminalWordLike(const int freq, int32_t* newWord, const in
// Will find the highest frequency of the words like the one passed as an argument,
// that is, everything that only differs by case/accents.
int UnigramDictionary::getMostFrequentWordLikeInner(const uint16_t * const inWord,
- const int length, short unsigned int* outWord) {
+ const int length, short unsigned int* outWord) const {
int32_t newWord[MAX_WORD_LENGTH_INTERNAL];
int depth = 0;
int maxFreq = -1;
const uint8_t* const root = DICT_ROOT;
+ int stackChildCount[MAX_WORD_LENGTH_INTERNAL];
+ int stackInputIndex[MAX_WORD_LENGTH_INTERNAL];
+ int stackSiblingPos[MAX_WORD_LENGTH_INTERNAL];
int startPos = 0;
- mStackChildCount[0] = BinaryFormat::getGroupCountAndForwardPointer(root, &startPos);
- mStackInputIndex[0] = 0;
- mStackSiblingPos[0] = startPos;
+ stackChildCount[0] = BinaryFormat::getGroupCountAndForwardPointer(root, &startPos);
+ stackInputIndex[0] = 0;
+ stackSiblingPos[0] = startPos;
while (depth >= 0) {
- const int charGroupCount = mStackChildCount[depth];
- int pos = mStackSiblingPos[depth];
+ const int charGroupCount = stackChildCount[depth];
+ int pos = stackSiblingPos[depth];
for (int charGroupIndex = charGroupCount - 1; charGroupIndex >= 0; --charGroupIndex) {
- int inputIndex = mStackInputIndex[depth];
+ int inputIndex = stackInputIndex[depth];
const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
// Test whether all chars in this group match with the word we are searching for. If so,
// we want to traverse its children (or if the length match, evaluate its frequency).
@@ -785,15 +791,15 @@ int UnigramDictionary::getMostFrequentWordLikeInner(const uint16_t * const inWor
// anyway, so don't traverse unless inputIndex < length.
if (isAlike && (-1 != childrenNodePos) && (inputIndex < length)) {
// Save position for this depth, to get back to this once children are done
- mStackChildCount[depth] = charGroupIndex;
- mStackSiblingPos[depth] = siblingPos;
+ stackChildCount[depth] = charGroupIndex;
+ stackSiblingPos[depth] = siblingPos;
// Prepare stack values for next depth
++depth;
int childrenPos = childrenNodePos;
- mStackChildCount[depth] =
+ stackChildCount[depth] =
BinaryFormat::getGroupCountAndForwardPointer(root, &childrenPos);
- mStackSiblingPos[depth] = childrenPos;
- mStackInputIndex[depth] = inputIndex;
+ stackSiblingPos[depth] = childrenPos;
+ stackInputIndex[depth] = inputIndex;
pos = childrenPos;
// Go to the next depth level.
++depth;
@@ -848,7 +854,7 @@ int UnigramDictionary::getBigramPosition(int pos, unsigned short *word, int offs
inline bool UnigramDictionary::processCurrentNode(const int initialPos,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter, Correction *correction,
int *newCount, int *newChildrenPosition, int *nextSiblingPosition,
- WordsPriorityQueuePool *queuePool, const int currentWordIndex) {
+ WordsPriorityQueuePool *queuePool, const int currentWordIndex) const {
if (DEBUG_DICT) {
correction->checkState();
}
diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h
index a1a8299e5..1b26eff10 100644
--- a/native/jni/src/unigram_dictionary.h
+++ b/native/jni/src/unigram_dictionary.h
@@ -81,7 +81,7 @@ class UnigramDictionary {
Correction *correction, const int *xcoordinates, const int *ycoordinates,
const int *codes, const int codesSize, const std::map<int, int> *bigramMap,
const uint8_t *bigramFilter, const bool useFullEditDistance, unsigned short *outWords,
- int *frequencies);
+ int *frequencies) const;
virtual ~UnigramDictionary();
private:
@@ -89,7 +89,7 @@ class UnigramDictionary {
const int *ycoordinates, const int *codes, const int inputLength,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
const bool useFullEditDistance, Correction *correction,
- WordsPriorityQueuePool *queuePool);
+ WordsPriorityQueuePool *queuePool) const;
int getDigraphReplacement(const int *codes, const int i, const int codesSize,
const digraph_t* const digraphs, const unsigned int digraphsSize) const;
void getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo,
@@ -99,37 +99,36 @@ class UnigramDictionary {
const bool useFullEditDistance, const int* codesSrc, const int codesRemain,
const int currentDepth, int* codesDest, Correction *correction,
WordsPriorityQueuePool* queuePool, const digraph_t* const digraphs,
- const unsigned int digraphsSize);
+ const unsigned int digraphsSize) const;
void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
- const int *ycoordinates, const int *codes, const int codesSize, Correction *correction);
+ const int *ycoordinates, const int *codes, const int codesSize,
+ Correction *correction) const;
void getOneWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
const int *ycoordinates, const int *codes, const std::map<int, int> *bigramMap,
const uint8_t *bigramFilter, const bool useFullEditDistance, const int inputLength,
- Correction *correction, WordsPriorityQueuePool* queuePool);
+ Correction *correction, WordsPriorityQueuePool* queuePool) const;
void getSuggestionCandidates(
const bool useFullEditDistance, const int inputLength,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
Correction *correction, WordsPriorityQueuePool* queuePool, const bool doAutoCompletion,
- const int maxErrors, const int currentWordIndex);
+ const int maxErrors, const int currentWordIndex) const;
void getSplitMultipleWordsSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const bool useFullEditDistance, const int inputLength,
Correction *correction, WordsPriorityQueuePool* queuePool,
- const bool hasAutoCorrectionCandidate);
+ const bool hasAutoCorrectionCandidate) const;
void onTerminal(const int freq, const TerminalAttributes& terminalAttributes,
Correction *correction, WordsPriorityQueuePool *queuePool, const bool addToMasterQueue,
- const int currentWordIndex);
- bool needsToSkipCurrentNode(const unsigned short c,
- const int inputIndex, const int skipPos, const int depth);
+ const int currentWordIndex) const;
// Process a node by considering proximity, missing and excessive character
bool processCurrentNode(const int initialPos, const std::map<int, int> *bigramMap,
const uint8_t *bigramFilter, Correction *correction, int *newCount,
int *newChildPosition, int *nextSiblingPosition, WordsPriorityQueuePool *queuePool,
- const int currentWordIndex);
+ const int currentWordIndex) const;
int getMostFrequentWordLike(const int startInputIndex, const int inputLength,
- ProximityInfo *proximityInfo, unsigned short *word);
+ Correction *correction, unsigned short *word) const;
int getMostFrequentWordLikeInner(const uint16_t* const inWord, const int length,
- short unsigned int *outWord);
+ short unsigned int *outWord) const;
int getSubStringSuggestion(
ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates,
const int *codes, const bool useFullEditDistance, Correction *correction,
@@ -137,14 +136,14 @@ class UnigramDictionary {
const bool hasAutoCorrectionCandidate, const int currentWordIndex,
const int inputWordStartPos, const int inputWordLength,
const int outputWordStartPos, const bool isSpaceProximity, int *freqArray,
- int *wordLengthArray, unsigned short* outputWord, int *outputWordLength);
+ int *wordLengthArray, unsigned short* outputWord, int *outputWordLength) const;
void getMultiWordsSuggestionRec(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const bool useFullEditDistance, const int inputLength,
Correction *correction, WordsPriorityQueuePool* queuePool,
const bool hasAutoCorrectionCandidate, const int startPos, const int startWordIndex,
const int outputWordLength, int *freqArray, int* wordLengthArray,
- unsigned short* outputWord);
+ unsigned short* outputWord) const;
const uint8_t* const DICT_ROOT;
const int MAX_WORD_LENGTH;
@@ -158,12 +157,6 @@ class UnigramDictionary {
static const digraph_t GERMAN_UMLAUT_DIGRAPHS[];
static const digraph_t FRENCH_LIGATURES_DIGRAPHS[];
-
- // Still bundled members
- unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
- int mStackChildCount[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
- int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
- int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL];// TODO: remove
};
} // namespace latinime
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
index fa067f4c8..3dc4543c2 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
@@ -16,25 +16,27 @@
package com.android.inputmethod.keyboard.internal;
-import android.test.AndroidTestCase;
+import android.app.Instrumentation;
+import android.test.InstrumentationTestCase;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
-public class KeySpecParserCsvTests extends AndroidTestCase {
+public class KeySpecParserCsvTests extends InstrumentationTestCase {
private final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@Override
protected void setUp() throws Exception {
super.setUp();
+ final Instrumentation instrumentation = getInstrumentation();
mTextsSet.setLanguage(Locale.ENGLISH.getLanguage());
- mTextsSet.loadStringResources(getContext());
+ mTextsSet.loadStringResources(instrumentation.getTargetContext());
final String[] testResourceNames = getAllResourceIdNames(
com.android.inputmethod.latin.tests.R.string.class);
- mTextsSet.loadStringResourcesInternal(getTestContext(),
+ mTextsSet.loadStringResourcesInternal(instrumentation.getContext(),
testResourceNames,
com.android.inputmethod.latin.tests.R.string.empty_string);
}
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
new file mode 100644
index 000000000..9ce581df8
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.test.AndroidTestCase;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+
+import com.android.inputmethod.latin.RichInputConnection.Range;
+
+public class RichInputConnectionTests extends AndroidTestCase {
+
+ // The following is meant to be a reasonable default for
+ // the "word_separators" resource.
+ private static final String sSeparators = ".,:;!?-";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ private class MockConnection extends InputConnectionWrapper {
+ final String mTextBefore;
+ final String mTextAfter;
+ final ExtractedText mExtractedText;
+
+ public MockConnection(String textBefore, String textAfter, ExtractedText extractedText) {
+ super(null, false);
+ mTextBefore = textBefore;
+ mTextAfter = textAfter;
+ mExtractedText = extractedText;
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.inputmethod.InputConnectionWrapper#getTextBeforeCursor(int, int)
+ */
+ @Override
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ return mTextBefore;
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.inputmethod.InputConnectionWrapper#getTextAfterCursor(int, int)
+ */
+ @Override
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ return mTextAfter;
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.inputmethod.InputConnectionWrapper#getExtractedText(
+ * ExtractedTextRequest, int)
+ */
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ return mExtractedText;
+ }
+
+ @Override
+ public boolean beginBatchEdit() {
+ return true;
+ }
+
+ @Override
+ public boolean endBatchEdit() {
+ return true;
+ }
+ }
+
+ /************************** Tests ************************/
+
+ /**
+ * Test for getting previous word (for bigram suggestions)
+ */
+ public void testGetPreviousWord() {
+ // If one of the following cases breaks, the bigram suggestions won't work.
+ assertEquals(RichInputConnection.getPreviousWord("abc def", sSeparators), "abc");
+ assertNull(RichInputConnection.getPreviousWord("abc", sSeparators));
+ assertNull(RichInputConnection.getPreviousWord("abc. def", sSeparators));
+
+ // The following tests reflect the current behavior of the function
+ // RichInputConnection#getPreviousWord.
+ // TODO: However at this time, the code does never go
+ // into such a path, so it should be safe to change the behavior of
+ // this function if needed - especially since it does not seem very
+ // logical. These tests are just there to catch any unintentional
+ // changes in the behavior of the RichInputConnection#getPreviousWord method.
+ assertEquals(RichInputConnection.getPreviousWord("abc def ", sSeparators), "abc");
+ assertEquals(RichInputConnection.getPreviousWord("abc def.", sSeparators), "abc");
+ assertEquals(RichInputConnection.getPreviousWord("abc def .", sSeparators), "def");
+ assertNull(RichInputConnection.getPreviousWord("abc ", sSeparators));
+ }
+
+ /**
+ * Test for getting the word before the cursor (for bigram)
+ */
+ public void testGetThisWord() {
+ assertEquals(RichInputConnection.getThisWord("abc def", sSeparators), "def");
+ assertEquals(RichInputConnection.getThisWord("abc def ", sSeparators), "def");
+ assertNull(RichInputConnection.getThisWord("abc def.", sSeparators));
+ assertNull(RichInputConnection.getThisWord("abc def .", sSeparators));
+ }
+
+ /**
+ * Test logic in getting the word range at the cursor.
+ */
+ public void testGetWordRangeAtCursor() {
+ ExtractedText et = new ExtractedText();
+ final RichInputConnection ic = new RichInputConnection();
+ InputConnection mockConnection;
+ mockConnection = new MockConnection("word wo", "rd", et);
+ et.startOffset = 0;
+ et.selectionStart = 7;
+ Range r;
+
+ ic.beginBatchEdit(mockConnection);
+ // basic case
+ r = ic.getWordRangeAtCursor(" ", 0);
+ assertEquals("word", r.mWord);
+
+ // more than one word
+ r = ic.getWordRangeAtCursor(" ", 1);
+ assertEquals("word word", r.mWord);
+ ic.endBatchEdit();
+
+ // tab character instead of space
+ mockConnection = new MockConnection("one\tword\two", "rd", et);
+ ic.beginBatchEdit(mockConnection);
+ r = ic.getWordRangeAtCursor("\t", 1);
+ ic.endBatchEdit();
+ assertEquals("word\tword", r.mWord);
+
+ // only one word doesn't go too far
+ mockConnection = new MockConnection("one\tword\two", "rd", et);
+ ic.beginBatchEdit(mockConnection);
+ r = ic.getWordRangeAtCursor("\t", 1);
+ ic.endBatchEdit();
+ assertEquals("word\tword", r.mWord);
+
+ // tab or space
+ mockConnection = new MockConnection("one word\two", "rd", et);
+ ic.beginBatchEdit(mockConnection);
+ r = ic.getWordRangeAtCursor(" \t", 1);
+ ic.endBatchEdit();
+ assertEquals("word\tword", r.mWord);
+
+ // tab or space multiword
+ mockConnection = new MockConnection("one word\two", "rd", et);
+ ic.beginBatchEdit(mockConnection);
+ r = ic.getWordRangeAtCursor(" \t", 2);
+ ic.endBatchEdit();
+ assertEquals("one word\tword", r.mWord);
+
+ // splitting on supplementary character
+ final String supplementaryChar = "\uD840\uDC8A";
+ mockConnection = new MockConnection("one word" + supplementaryChar + "wo", "rd", et);
+ ic.beginBatchEdit(mockConnection);
+ r = ic.getWordRangeAtCursor(supplementaryChar, 0);
+ ic.endBatchEdit();
+ assertEquals("word", r.mWord);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/UtilsTests.java b/tests/src/com/android/inputmethod/latin/UtilsTests.java
deleted file mode 100644
index 2ef4e2ff5..000000000
--- a/tests/src/com/android/inputmethod/latin/UtilsTests.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010,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.test.AndroidTestCase;
-
-public class UtilsTests extends AndroidTestCase {
-
- // The following is meant to be a reasonable default for
- // the "word_separators" resource.
- private static final String sSeparators = ".,:;!?-";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- /************************** Tests ************************/
-
- /**
- * Test for getting previous word (for bigram suggestions)
- */
- public void testGetPreviousWord() {
- // If one of the following cases breaks, the bigram suggestions won't work.
- assertEquals(EditingUtils.getPreviousWord("abc def", sSeparators), "abc");
- assertNull(EditingUtils.getPreviousWord("abc", sSeparators));
- assertNull(EditingUtils.getPreviousWord("abc. def", sSeparators));
-
- // The following tests reflect the current behavior of the function
- // EditingUtils#getPreviousWord.
- // TODO: However at this time, the code does never go
- // into such a path, so it should be safe to change the behavior of
- // this function if needed - especially since it does not seem very
- // logical. These tests are just there to catch any unintentional
- // changes in the behavior of the EditingUtils#getPreviousWord method.
- assertEquals(EditingUtils.getPreviousWord("abc def ", sSeparators), "abc");
- assertEquals(EditingUtils.getPreviousWord("abc def.", sSeparators), "abc");
- assertEquals(EditingUtils.getPreviousWord("abc def .", sSeparators), "def");
- assertNull(EditingUtils.getPreviousWord("abc ", sSeparators));
- }
-
- /**
- * Test for getting the word before the cursor (for bigram)
- */
- public void testGetThisWord() {
- assertEquals(EditingUtils.getThisWord("abc def", sSeparators), "def");
- assertEquals(EditingUtils.getThisWord("abc def ", sSeparators), "def");
- assertNull(EditingUtils.getThisWord("abc def.", sSeparators));
- assertNull(EditingUtils.getThisWord("abc def .", sSeparators));
- }
-}
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
new file mode 100644
index 000000000..9e8dbe0f8
--- /dev/null
+++ b/tools/dicttool/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+LOCAL_MODULE := dicttool
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+include $(LOCAL_PATH)/etc/Android.mk
diff --git a/tools/dicttool/etc/Android.mk b/tools/dicttool/etc/Android.mk
new file mode 100644
index 000000000..03d4a96ee
--- /dev/null
+++ b/tools/dicttool/etc/Android.mk
@@ -0,0 +1,20 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := eng
+LOCAL_PREBUILT_EXECUTABLES := dicttool
+include $(BUILD_HOST_PREBUILT)
diff --git a/tools/dicttool/etc/dicttool b/tools/dicttool/etc/dicttool
new file mode 100755
index 000000000..8a39694f7
--- /dev/null
+++ b/tools/dicttool/etc/dicttool
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Copyright 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dicttool.jar
+frameworkdir="$progdir"
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ frameworkdir=`dirname "$progdir"`/tools/lib
+ libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ frameworkdir=`dirname "$progdir"`/framework
+ libdir=`dirname "$progdir"`/lib
+fi
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$frameworkdir/$jarfile"`
+ progdir=`cygpath -w "$progdir"`
+else
+ jarpath="$frameworkdir/$jarfile"
+fi
+
+# might need more memory, e.g. -Xmx128M
+exec java -ea -jar "$jarpath" "$@"
diff --git a/tools/dicttool/etc/manifest.txt b/tools/dicttool/etc/manifest.txt
new file mode 100644
index 000000000..67c85214c
--- /dev/null
+++ b/tools/dicttool/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.inputmethod.latin.dicttool.Dicttool
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java
new file mode 100644
index 000000000..307f5964c
--- /dev/null
+++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java
@@ -0,0 +1,94 @@
+/**
+ * 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.dicttool;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class Compress {
+
+ private static OutputStream getCompressedStream(final OutputStream out)
+ throws java.io.IOException {
+ return new GZIPOutputStream(out);
+ }
+
+ private static InputStream getUncompressedStream(final InputStream in) throws IOException {
+ return new GZIPInputStream(in);
+ }
+
+ public static void copy(final InputStream input, final OutputStream output) throws IOException {
+ final byte[] buffer = new byte[1000];
+ for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer))
+ output.write(buffer, 0, readBytes);
+ input.close();
+ output.close();
+ }
+
+ static public class Compressor extends Dicttool.Command {
+ public static final String COMMAND = "compress";
+ private static final String SUFFIX = ".compressed";
+
+ public Compressor() {
+ }
+
+ public String getHelp() {
+ return "compress <filename>: Compresses a file using gzip compression";
+ }
+
+ public int getArity() {
+ return 1;
+ }
+
+ public void run() throws IOException {
+ final String inFilename = mArgs[0];
+ final String outFilename = inFilename + SUFFIX;
+ final FileInputStream input = new FileInputStream(new File(inFilename));
+ final FileOutputStream output = new FileOutputStream(new File(outFilename));
+ copy(input, new GZIPOutputStream(output));
+ }
+ }
+
+ static public class Uncompressor extends Dicttool.Command {
+ public static final String COMMAND = "uncompress";
+ private static final String SUFFIX = ".uncompressed";
+
+ public Uncompressor() {
+ }
+
+ public String getHelp() {
+ return "uncompress <filename>: Uncompresses a file compressed with gzip compression";
+ }
+
+ public int getArity() {
+ return 1;
+ }
+
+ public void run() throws IOException {
+ final String inFilename = mArgs[0];
+ final String outFilename = inFilename + SUFFIX;
+ final FileInputStream input = new FileInputStream(new File(inFilename));
+ final FileOutputStream output = new FileOutputStream(new File(outFilename));
+ copy(new GZIPInputStream(input), output);
+ }
+ }
+}
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java
new file mode 100644
index 000000000..b78be7975
--- /dev/null
+++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java
@@ -0,0 +1,120 @@
+/**
+ * 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.dicttool;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class Dicttool {
+
+ public static abstract class Command {
+ protected String[] mArgs;
+ public void setArgs(String[] args) throws IllegalArgumentException {
+ mArgs = args;
+ }
+ abstract public int getArity();
+ abstract public String getHelp();
+ abstract public void run() throws Exception;
+ }
+ static HashMap<String, Class<? extends Command>> sCommands =
+ new HashMap<String, Class<? extends Command>>();
+ static {
+ sCommands.put("info", Info.class);
+ sCommands.put("compress", Compress.Compressor.class);
+ sCommands.put("uncompress", Compress.Uncompressor.class);
+ }
+
+ private static Command getCommandInstance(final String commandName) {
+ try {
+ return sCommands.get(commandName).newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(commandName + " is not installed");
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(commandName + " is not installed");
+ }
+ }
+
+ private static void help() {
+ System.out.println("Syntax: dicttool <command [arguments]>\nAvailable commands:\n");
+ for (final String commandName : sCommands.keySet()) {
+ System.out.println("*** " + commandName);
+ System.out.println(getCommandInstance(commandName).getHelp());
+ System.out.println("");
+ }
+ }
+
+ private static boolean isCommand(final String commandName) {
+ return sCommands.containsKey(commandName);
+ }
+
+ private String mPreviousCommand = null; // local to the getNextCommand function
+ private Command getNextCommand(final ArrayList<String> arguments) {
+ final String firstArgument = arguments.get(0);
+ final String commandName;
+ if (isCommand(firstArgument)) {
+ commandName = firstArgument;
+ arguments.remove(0);
+ } else if (isCommand(mPreviousCommand)) {
+ commandName = mPreviousCommand;
+ } else {
+ throw new RuntimeException("Unknown command : " + firstArgument);
+ }
+ final Command command = getCommandInstance(commandName);
+ final int arity = command.getArity();
+ if (arguments.size() < arity) {
+ throw new RuntimeException("Not enough arguments to command " + commandName);
+ }
+ final String[] argsArray = new String[arity];
+ arguments.subList(0, arity).toArray(argsArray);
+ for (int i = 0; i < arity; ++i) {
+ // For some reason, ArrayList#removeRange is protected
+ arguments.remove(0);
+ }
+ command.setArgs(argsArray);
+ mPreviousCommand = commandName;
+ return command;
+ }
+
+ private void execute(final ArrayList<String> arguments) {
+ ArrayList<Command> commandsToExecute = new ArrayList<Command>();
+ while (!arguments.isEmpty()) {
+ commandsToExecute.add(getNextCommand(arguments));
+ }
+ for (final Command command : commandsToExecute) {
+ try {
+ command.run();
+ } catch (Exception e) {
+ System.out.println("Exception while processing command "
+ + command.getClass().getSimpleName() + " : " + e);
+ return;
+ }
+ }
+ }
+
+ public static void main(final String[] args) {
+ if (0 == args.length) {
+ help();
+ return;
+ }
+ if (!isCommand(args[0])) throw new RuntimeException("Unknown command : " + args[0]);
+
+ final ArrayList<String> arguments = new ArrayList<String>(args.length);
+ arguments.addAll(Arrays.asList(args));
+ new Dicttool().execute(arguments);
+ }
+}
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java
new file mode 100644
index 000000000..cb032dd3b
--- /dev/null
+++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java
@@ -0,0 +1,35 @@
+/**
+ * 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.dicttool;
+
+public class Info extends Dicttool.Command {
+ public Info() {
+ }
+
+ public String getHelp() {
+ return "info <filename>: prints various information about a dictionary file";
+ }
+
+ public int getArity() {
+ return 1;
+ }
+
+ public void run() {
+ // TODO: implement this
+ System.out.println("Not implemented yet");
+ }
+}
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java b/tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java
index 366d73e20..07a6c300e 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java
+++ b/tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java
@@ -27,14 +27,13 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarUtils {
- private static final String MANIFEST = "META-INF/MANIFEST.MF";
-
private JarUtils() {
// This utility class is not publicly instantiable.
}
- public static JarFile getJarFile(final ClassLoader loader) {
- final URL resUrl = loader.getResource(MANIFEST);
+ public static JarFile getJarFile(final Class<?> mainClass) {
+ final String mainClassPath = "/" + mainClass.getName().replace('.', '/') + ".class";
+ final URL resUrl = mainClass.getResource(mainClassPath);
if (!resUrl.getProtocol().equals("jar")) {
throw new RuntimeException("Should run as jar");
}
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java b/tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java
index a5abcf1c1..4a9236962 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java
+++ b/tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java
@@ -58,7 +58,7 @@ public class LabelText {
public static void main(final String[] args) {
final Options options = new Options(args);
- final JarFile jar = JarUtils.getJarFile(LabelText.class.getClassLoader());
+ final JarFile jar = JarUtils.getJarFile(LabelText.class);
final MoreKeysResources resources = new MoreKeysResources(jar);
resources.writeToJava(options.mJava);
}
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java b/tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java
index a4835932b..37ac0d006 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java
+++ b/tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java
@@ -100,7 +100,7 @@ public class MoreKeysResources {
final File outputFile = new File(outPackage,
JAVA_TEMPLATE.replace(".tmpl", ".java"));
outPackage.mkdirs();
- ps = new PrintStream(outputFile);
+ ps = new PrintStream(outputFile, "UTF-8");
}
lnr = new LineNumberReader(new InputStreamReader(JarUtils.openResource(template)));
inflateTemplate(lnr, ps);