diff options
211 files changed, 7117 insertions, 4377 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index cb0a9a2e1..548da1bbb 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -21,12 +21,28 @@ <meta-data android:name="android.view.im" android:resource="@xml/method" /> </service> + <service android:name=".spellcheck.AndroidSpellCheckerService" + android:label="@string/spell_checker_service_name" + android:permission="android.permission.BIND_TEXT_SERVICE"> + <intent-filter> + <action android:name="android.service.textservice.SpellCheckerService" /> + </intent-filter> + <meta-data android:name="android.view.textservice.scs" android:resource="@xml/spellchecker" /> + </service> + <activity android:name="Settings" android:label="@string/english_ime_settings"> <intent-filter> <action android:name="android.intent.action.MAIN"/> </intent-filter> </activity> + <activity android:name="com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsActivity" + android:label="@string/android_spell_checker_settings"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + </intent-filter> + </activity> + <activity android:name="DebugSettings" android:label="@string/english_ime_debug_settings"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/java/proguard.flags b/java/proguard.flags index 7ce6f41b5..33af890ec 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -27,6 +27,14 @@ *; } +-keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment { + *; +} + -keep class com.android.inputmethod.latin.SettingsActivity { *; } + +-keep class com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder$MiniKeyboardParams { + <init>(...); +} diff --git a/java/res/drawable-hdpi/keyboard_background_holo.9.png b/java/res/drawable-hdpi/keyboard_background_holo.9.png Binary files differindex 9f700c20c..714db4305 100644 --- a/java/res/drawable-hdpi/keyboard_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png Binary files differindex 85b6360c5..e173beb73 100644 --- a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png diff --git a/java/res/drawable-hdpi/more_suggestions_hint.png b/java/res/drawable-hdpi/more_suggestions_hint.png Binary files differnew file mode 100644 index 000000000..4515f4434 --- /dev/null +++ b/java/res/drawable-hdpi/more_suggestions_hint.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png Binary files differdeleted file mode 100644 index 3e4eff698..000000000 --- a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png Binary files differnew file mode 100644 index 000000000..25702cf73 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png diff --git a/java/res/drawable-hdpi/sym_keyboard_123_mic.png b/java/res/drawable-hdpi/sym_keyboard_123_mic.png Binary files differdeleted file mode 100644 index 6f0aec24c..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_123_mic.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_123_mic_holo.png b/java/res/drawable-hdpi/sym_keyboard_123_mic_holo.png Binary files differdeleted file mode 100644 index ed66f7520..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_123_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic.png b/java/res/drawable-hdpi/sym_keyboard_label_mic.png Binary files differnew file mode 100644 index 000000000..4e0a8ed8e --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_label_mic.png diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png Binary files differnew file mode 100644 index 000000000..22802433e --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png diff --git a/java/res/drawable-mdpi/keyboard_background_holo.9.png b/java/res/drawable-mdpi/keyboard_background_holo.9.png Binary files differindex 4fe79b936..2776621b1 100644 --- a/java/res/drawable-mdpi/keyboard_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png Binary files differindex e488323c9..b1c18b412 100644 --- a/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_suggest_strip_holo.9.png diff --git a/java/res/drawable-mdpi/more_suggestions_hint.png b/java/res/drawable-mdpi/more_suggestions_hint.png Binary files differnew file mode 100644 index 000000000..6168de353 --- /dev/null +++ b/java/res/drawable-mdpi/more_suggestions_hint.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png Binary files differdeleted file mode 100644 index 0749b5fc6..000000000 --- a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png Binary files differnew file mode 100644 index 000000000..7f0b1355f --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png diff --git a/java/res/drawable-mdpi/sym_keyboard_123_mic.png b/java/res/drawable-mdpi/sym_keyboard_123_mic.png Binary files differdeleted file mode 100644 index 35afe0821..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_123_mic.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_123_mic_holo.png b/java/res/drawable-mdpi/sym_keyboard_123_mic_holo.png Binary files differdeleted file mode 100644 index 4814e5141..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_123_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic.png b/java/res/drawable-mdpi/sym_keyboard_label_mic.png Binary files differnew file mode 100644 index 000000000..a354d5321 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_label_mic.png diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png Binary files differnew file mode 100644 index 000000000..d51adbe2f --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png diff --git a/java/res/drawable-xhdpi/keyboard_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_background_holo.9.png Binary files differindex 7b79f60ad..a0aa4ba22 100644 --- a/java/res/drawable-xhdpi/keyboard_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png Binary files differindex b40f76662..4c27072e9 100644 --- a/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_holo.9.png diff --git a/java/res/drawable-xhdpi/more_suggestions_hint.png b/java/res/drawable-xhdpi/more_suggestions_hint.png Binary files differnew file mode 100644 index 000000000..f577a3617 --- /dev/null +++ b/java/res/drawable-xhdpi/more_suggestions_hint.png diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png Binary files differdeleted file mode 100644 index 0aefaa1c3..000000000 --- a/java/res/drawable-xhdpi/sym_bkeyboard_123_mic.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png Binary files differnew file mode 100644 index 000000000..9bd1d65b4 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_123_mic.png b/java/res/drawable-xhdpi/sym_keyboard_123_mic.png Binary files differdeleted file mode 100644 index bcb097967..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_123_mic.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_123_mic_holo.png b/java/res/drawable-xhdpi/sym_keyboard_123_mic_holo.png Binary files differdeleted file mode 100644 index 0c7505874..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_123_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic.png Binary files differnew file mode 100644 index 000000000..49810a02f --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_label_mic.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png Binary files differnew file mode 100644 index 000000000..a7d3eaa0a --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png diff --git a/java/res/layout/candidates_strip.xml b/java/res/layout/candidates_strip.xml index 269bc1bc3..bcc1322b6 100644 --- a/java/res/layout/candidates_strip.xml +++ b/java/res/layout/candidates_strip.xml @@ -29,31 +29,6 @@ android:layout_width="0dp" android:layout_height="match_parent" /> <LinearLayout - android:id="@+id/candidates_pane_control" - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="match_parent" - > - <TextView - android:id="@+id/expand_candidates_pane" - android:text="@string/label_expand_candidates_pane" - android:gravity="center" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:minWidth="28dp" - android:textSize="@dimen/candidate_text_size" - android:visibility="visible" /> - <TextView - android:id="@+id/close_candidates_pane" - android:text="@string/label_close_candidates_pane" - android:gravity="center" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:minWidth="28dp" - android:textSize="@dimen/candidate_text_size" - android:visibility="gone" /> - </LinearLayout> - <LinearLayout android:id="@+id/touch_to_save" android:orientation="horizontal" android:layout_width="match_parent" diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 239c80027..8d3632826 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android-sleutelbord"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android-sleutelbordinstellings"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreer met sleuteldruk"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Klank met sleuteldruk"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Opspring by druk van sleutel"</string> @@ -58,9 +66,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Wys in portretmodus"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Versteek altyd"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Wys instellingsleutel"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Outomaties"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Wys altyd"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Versteek altyd"</string> <string name="auto_correction" msgid="4979925752001319458">"Outokorrigering"</string> <!-- outdated translation 6881047311475758267 --> <string name="auto_correction_summary" msgid="5625751551134658006">"Korrigeer outomaties die vorige woord"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Af"</string> @@ -80,6 +85,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Klaar"</string> <string name="label_send_key" msgid="2815056534433717444">"Stuur"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <!-- no translation found for label_to_symbol_key (8516904117128967293) --> + <skip /> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Meer"</string> <string name="label_pause_key" msgid="181098308428035340">"Laat wag"</string> <string name="label_wait_key" msgid="6402152600878093134">"Wag"</string> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 54793048a..16595d22a 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"የAndroid ቁልፍሰሌዳ"</string> <string name="english_ime_settings" msgid="6661589557206947774">"የAndroid ቁልፍሰሌዳ ቅንብሮች"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"በቁልፍ መጫን ላይ የሚወጣ ድምፅ"</string> <string name="popup_on_keypress" msgid="123894815723512944">"ቁልፍ ጫን ላይ ብቅ ባይ"</string> @@ -58,9 +66,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"በቁመት ሁነታ አሳይ"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ሁልጊዜ ደብቅ"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"የቅንብሮች ቁልፍ አሳይ"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"ራስ ሰር"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"ሁልጊዜ አሳይ"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"ሁልጊዜ ደብቅ"</string> <string name="auto_correction" msgid="4979925752001319458">"በራስ ማስተካከል"</string> <!-- outdated translation 6881047311475758267 --> <string name="auto_correction_summary" msgid="5625751551134658006">"የቀደመውን ቃል በራስሰር አስተካክል"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ውጪ"</string> @@ -80,6 +85,9 @@ <string name="label_done_key" msgid="2441578748772529288">"ተከናውኗል"</string> <string name="label_send_key" msgid="2815056534433717444">" ይላኩ"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <!-- no translation found for label_to_symbol_key (8516904117128967293) --> + <skip /> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"ተጨማሪ"</string> <string name="label_pause_key" msgid="181098308428035340">"ላፍታ አቁም"</string> <string name="label_wait_key" msgid="6402152600878093134">"ቆይ"</string> diff --git a/java/res/values-ar/donottranslate-altchars.xml b/java/res/values-ar/donottranslate-altchars.xml index b72ed501a..0e4f265e7 100644 --- a/java/res/values-ar/donottranslate-altchars.xml +++ b/java/res/values-ar/donottranslate-altchars.xml @@ -31,9 +31,7 @@ \u064b: ARABIC FATHATAN \u064d: ARABIC KASRATAN --> <string name="alternates_for_punctuation">"\u060c,\u061b,\u061f,!,:,-,/,\',\",\u0651,\u0652,\u064c,\u0640,\u064f,\u064e,\u0650,\u064b,\u064d"</string> - <string name="alternates_for_web_tab_punctuation">".,\u060c,\u061b,\u061f,!,:,-,/,\',\",\u0651,\u0652,\u064c,\u0640,\u064f,\u064e,\u0650,\u064b,\u064d"</string> <integer name="popup_keyboard_column_for_punctuation">9</integer> - <integer name="popup_keyboard_column_for_web_punctuation">10</integer> <string name="keyhintlabel_for_punctuation">\u064b</string> <string name="keylabel_for_symbols_1">"١"</string> <string name="keylabel_for_symbols_2">"٢"</string> @@ -55,15 +53,18 @@ <string name="alternates_for_symbols_8">8</string> <string name="alternates_for_symbols_9">9</string> <string name="alternates_for_symbols_0">0,ⁿ,∅</string> - <string name="keylabel_for_symbols_comma">\u060c</string> - <string name="keylabel_for_symbols_f1">\u060c</string> + <string name="keylabel_for_comma">\u060c</string> + <string name="keylabel_for_f1">\u060c</string> <string name="keylabel_for_symbols_question">\u061f</string> <string name="keylabel_for_symbols_semicolon">\u061b</string> <!-- \u066a: ARABIC PERCENT SIGN --> <string name="keylabel_for_symbols_percent">\u066a</string> - <string name="alternates_for_symbols_comma">,</string> - <!-- @icon/5 is iconsSettingsKey --> - <string name="alternates_for_symbols_f1">"\\,,\@icon/5|\@integer/key_settings"</string> + <string name="alternates_for_comma">,</string> + <string name="alternates_for_f1">,</string> + <!-- @icon/3 is iconSettingsKey --> + <string name="alternates_for_f1_settings">\\,,\@icon/3|\@integer/key_settings</string> + <!-- @icon/7 is iconTabKey --> + <string name="alternates_for_f1_navigate">\\,,\@icon/7|\@integer/key_tab</string> <string name="alternates_for_symbols_question">\?,¿</string> <string name="alternates_for_symbols_semicolon">;</string> <string name="alternates_for_symbols_percent">%,‰</string> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 935dd2b4f..46f0c1d77 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"لوحة مفاتيح Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"إعدادات لوحة مفاتيح Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"تصحيح Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"اهتزاز عند الضغط على مفتاح"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"صوت عند الضغط على مفتاح"</string> <string name="popup_on_keypress" msgid="123894815723512944">"انبثاق عند الضغط على المفاتيح"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"عرض في وضع رأسي"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"إخفاء دومًا"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"عرض مفتاح الإعدادات"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"تلقائي"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"إظهار بشكل دائم"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"إخفاء دومًا"</string> <string name="auto_correction" msgid="4979925752001319458">"التصحيح التلقائي"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"تؤدي المسافة والترقيم إلى تصحيح الكلمات المكتوبة بشكل غير صحيح"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"إيقاف"</string> @@ -63,7 +67,10 @@ <string name="label_next_key" msgid="362972844525672568">"التالي"</string> <string name="label_done_key" msgid="2441578748772529288">"تم"</string> <string name="label_send_key" msgid="2815056534433717444">"إرسال"</string> - <string name="label_to_alpha_key" msgid="4793983863798817523">"أ ب ج د"</string> + <string name="label_to_alpha_key" msgid="4793983863798817523">"أ ب ج"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"المزيد"</string> <string name="label_pause_key" msgid="181098308428035340">"توقف مؤقت"</string> <string name="label_wait_key" msgid="6402152600878093134">"انتظار"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ميكروفون على لوحة مفاتيح الرموز"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"الإدخال الصوتي مُعطل"</string> <string name="selectInputMethod" msgid="315076553378705821">"تحديد طريقة الإرسال"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"تهيئة طرق الإدخال"</string> <string name="language_selection_title" msgid="1651299598555326750">"لغات الإدخال"</string> <string name="select_language" msgid="2573265881207142437">"تحديد لغات الإدخال"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← المس مرة أخرى للحفظ"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"المس لتصحيح الكلمات"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"المس الكلمات التي تم إدخالها لتصحيحها، وذلك فقط عندما تكون الاقتراحات مرئية."</string> <string name="keyboard_layout" msgid="8451164783510487501">"مظهر لوحة المفاتيح"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"الألمانية (QWERTY)"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"الإنجليزية (المملكة المتحدة)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"الإنجليزية (الولايات المتحدة)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"وضع دراسة سهولة الاستخدام"</string> </resources> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 851b05b2d..f2514ecce 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура на Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Настройки на клавиатурата на Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Корекция на Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Да вибрира при натискане на клавиш"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натискане на клавиш"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Изскачащ прозорец при натискане на клавиш"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показване с вертикална ориентация"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Винаги да се скрива"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Показване на клавиша за настройки"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Автоматично"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Да се показва винаги"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Да се скрива винаги"</string> <string name="auto_correction" msgid="4979925752001319458">"Автоко"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Клавишът за интервал и пунктуация авт. поправя сгрешени думи"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Изкл."</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Готово"</string> <string name="label_send_key" msgid="2815056534433717444">"Изпращане"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"АБВ"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Още"</string> <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string> <string name="label_wait_key" msgid="6402152600878093134">"Чака"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Микр. на клав. на симв."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Глас. въвежд. е деакт."</string> <string name="selectInputMethod" msgid="315076553378705821">"Избор на метод на въвеждане"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Конфигуриране на въвеждането"</string> <string name="language_selection_title" msgid="1651299598555326750">"Входни езици"</string> <string name="select_language" msgid="2573265881207142437">"Избор на езици за въвеждане"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Докоснете отново, за да запазите"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Докоснете, за да поправите думите"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Докоснете въведените думи, за да ги поправите – само когато предложенията са видими"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема на клавиатурата"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Немска „QWERTY“"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"английски (Великобритания)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"английски (САЩ)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за изучаване на използваемостта"</string> </resources> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index d2a83536d..6daca172e 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Teclat Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Configuració del teclat d\'Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Correcció d\'Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra en prémer tecles"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"So en prémer una tecla"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Finestra emergent en prémer un botó"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostra en mode vertical"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Amaga sempre"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Mostra la tecla de configuració"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automàtic"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostra sempre"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Amaga sempre"</string> <string name="auto_correction" msgid="4979925752001319458">"Correcció automàtica"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Barra espaiadora i punt. correg. autom. paraules mal escrites"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactiva"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Fet"</string> <string name="label_send_key" msgid="2815056534433717444">"Envia"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Més"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Espera"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro en tecl. símb."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. veu desactiv."</string> <string name="selectInputMethod" msgid="315076553378705821">"Selecciona el mètode d\'entrada"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Selecciona l\'idioma d\'entrada"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Torna a tocar per desar"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Toca per corregir paraules"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toca les paraules introduïdes per corregir-les, només quan els suggeriments siguin visibles"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema del teclat"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemany"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Anglès (Regne Unit)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Anglès (EUA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'estudi d\'usabilitat"</string> </resources> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index 79c66b834..9cd237edd 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Klávesnice Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Opravy pravopisu Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk při stisku klávesy"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Zobrazit znaky při stisku klávesy"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Zobrazit v režimu na výšku"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývat"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Zobrazit klávesu Nastavení"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automaticky"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vždy zobrazovat"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vždy skrývat"</string> <string name="auto_correction" msgid="4979925752001319458">"Automatické opravy"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Stisknutím mezerníku a interpunkce se automaticky opravují chybně napsaná slova"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuto"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Hotovo"</string> <string name="label_send_key" msgid="2815056534433717444">"Odeslat"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Další"</string> <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string> <string name="label_wait_key" msgid="6402152600878093134">"Čekat"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. na kláv. se symb."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup vypnut"</string> <string name="selectInputMethod" msgid="315076553378705821">"Výběr metody zadávání dat"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurace metod vstupu"</string> <string name="language_selection_title" msgid="1651299598555326750">"Vstupní jazyky"</string> <string name="select_language" msgid="2573265881207142437">"Výběr jazyků vstupu"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Dalším dotykem slovo uložíte"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dotykem aktivovat opravy"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Klepnutím na zadaná slova tato slova opravíte, musí však být viditelné návrhy."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Motiv klávesnice"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"němčina (QWERTY)"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"angličtina (Spojené království)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"angličtina (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim studie použitelnosti"</string> </resources> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index a4ac4e115..85cbb85d1 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android-tastatur"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Indstillinger for Android-tastatur"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android-rettelse"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetryk"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Popup ved tastetryk"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Vis i portrættilstand"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul altid"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Vis indstillingsnøgle"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisk"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vis altid"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Skjul altid"</string> <string name="auto_correction" msgid="4979925752001319458">"Automatisk retning"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Mellemrumstast og tegnsætning retter automatisk forkerte ord"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Fra"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Udfør"</string> <string name="label_send_key" msgid="2815056534433717444">"Send"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Mere"</string> <string name="label_pause_key" msgid="181098308428035340">"Pause"</string> <string name="label_wait_key" msgid="6402152600878093134">"Vent"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. på symboltastatur"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Stemmeinput deaktiveret"</string> <string name="selectInputMethod" msgid="315076553378705821">"Vælg inputmetode"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inputmetoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inputsprog"</string> <string name="select_language" msgid="2573265881207142437">"Vælg inputsprog"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tryk igen for at gemme"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tryk for at rette ord"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tryk på de indtastede ord for at rette dem. Kun når der er synlige forslag."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturtema"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tysk QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannien)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tilstand for brugsstudie"</string> </resources> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index b2d5e9219..7a20f69c6 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android-Tastatur"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatureinstellungen"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrieren b. Tastendruck"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bei Tastendruck"</string> @@ -45,9 +53,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Im Hochformat anzeigen"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Immer ausblenden"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Einstellungstaste anz."</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisch"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Immer anzeigen"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Immer ausblenden"</string> <string name="auto_correction" msgid="4979925752001319458">"Autokorrektur"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Korrektur fehlerhafter Wörter durch Leertaste und Satzzeichen"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Aus"</string> @@ -64,6 +69,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Fertig"</string> <string name="label_send_key" msgid="2815056534433717444">"Senden"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Mehr"</string> <string name="label_pause_key" msgid="181098308428035340">"Pause"</string> <string name="label_wait_key" msgid="6402152600878093134">"Warten"</string> @@ -131,8 +139,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikro auf Symboltastatur"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spracheingabe deaktiviert"</string> <string name="selectInputMethod" msgid="315076553378705821">"Eingabemethode auswählen"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Eingabemethoden konfigurieren"</string> <string name="language_selection_title" msgid="1651299598555326750">"Eingabesprachen"</string> <string name="select_language" msgid="2573265881207142437">"Eingabesprachen auswählen"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Zum Speichern erneut berühren"</string> @@ -142,11 +149,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Wortkorrektur"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tippen Sie zum Korrigieren auf eingegebene Wörter (nur, wenn Vorschläge angezeigt werden)."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturdesign"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Deutsche QWERTZ-Tastaturbelegung"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Englisch (GB)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Englisch (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus der Studie zur Benutzerfreundlichkeit"</string> </resources> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 60a7d860f..e0c503341 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Πληκτρολόγιο Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Ρυθμίσεις πληκτρολογίου Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Διόρθωση Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Ήχος κατά το πάτημα πλήκτρων"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Εμφάνιση με το πάτημα πλήκτρου"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Εμφάνιση σε λειτουργία κατακόρυφης προβολής"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Πάντα απόκρυψη"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Εμφάνιση πλήκτρου ρυθμίσεων"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Αυτόματο"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Να εμφανίζεται πάντα"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Πάντα απόκρυψη"</string> <string name="auto_correction" msgid="4979925752001319458">"Αυτόματη διόρθωση"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Τα πλήκτρα διαστήματος και στίξης διορθ. αυτόμ. λάθος λέξεις"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Απενεργοποίηση"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Τέλος"</string> <string name="label_send_key" msgid="2815056534433717444">"Αποστολή"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ΑΒΓ"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">";123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Περισσότερα"</string> <string name="label_pause_key" msgid="181098308428035340">"Παύση"</string> <string name="label_wait_key" msgid="6402152600878093134">"Αναμ."</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Μικ. στο πληκ. συμβ."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Απεν. φωνητ. είσοδος"</string> <string name="selectInputMethod" msgid="315076553378705821">"Επιλογή μεθόδου εισόδου"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Διαμόρφωση μεθόδων εισαγωγής"</string> <string name="language_selection_title" msgid="1651299598555326750">"Γλώσσες εισόδου"</string> <string name="select_language" msgid="2573265881207142437">"Επιλογή γλωσσών εισαγωγής"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Αγγίξτε ξανά για αποθήκευση"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Αγγίξτε για διόρθωση λέξεων"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Αγγίξτε τις λέξες για να τις διορθώσετε, μόνο όταν οι προτάσεις είναι ορατές"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Θέμα πληκτρολογίου"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Γερμανικά QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Αγγλικά (ΗΒ)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Αγγλικά (ΗΠΑ)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Λειτουργία μελέτης χρηστικότητας"</string> </resources> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index a4930eae9..f89c29304 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android keyboard settings"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android correction"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on key-press"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Sound on key-press"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up on key press"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Show on portrait mode"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Always hide"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Show settings key"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatic"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Always show"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Always hide"</string> <string name="auto_correction" msgid="4979925752001319458">"Auto-correction"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Spacebar and punctuation correct mistyped words automatically"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Done"</string> <string name="label_send_key" msgid="2815056534433717444">"Send"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"More"</string> <string name="label_pause_key" msgid="181098308428035340">"Pause"</string> <string name="label_wait_key" msgid="6402152600878093134">"Wait"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic on symbols keyboard"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Voice input is disabled"</string> <string name="selectInputMethod" msgid="315076553378705821">"Select input method"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string> <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string> <string name="select_language" msgid="2573265881207142437">"Select input languages"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Touch again to save"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Touch to correct words"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Touch words entered to correct them, only when suggestions are visible"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Keyboard theme"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"German QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Usability study mode"</string> </resources> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 30e7aa6b1..87a15a145 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Corrector de Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Sonar al pulsar las teclas"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Aviso emergente al pulsar tecla"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar en modo retrato"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla de configuración"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automático"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar siempre"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ocultar siempre"</string> <string name="auto_correction" msgid="4979925752001319458">"Corrección automática"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"La barra espaciadora y puntuación insertan automáticamente las palabras corregidas"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivado"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Hecho"</string> <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Más"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Espera"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrófono en el teclado de símbolos"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"La entrada por voz está inhabilitada"</string> <string name="selectInputMethod" msgid="315076553378705821">"Seleccionar método de entrada"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Seleccionar idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tocar de nuevo para guardar"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corregir palabras"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toca las palabras ingresadas que desees corregir solo cuando las sugerencias estén visibles."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema del teclado"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemán"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Inglés (Reino Unido)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Inglés (EE.UU.)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudio de usabilidad"</string> </resources> diff --git a/java/res/values-es/donottranslate-altchars.xml b/java/res/values-es/donottranslate-altchars.xml index 16111ec42..e1f849f62 100644 --- a/java/res/values-es/donottranslate-altchars.xml +++ b/java/res/values-es/donottranslate-altchars.xml @@ -26,5 +26,4 @@ <string name="alternates_for_n">ñ,ń</string> <string name="alternates_for_c">ç,ć,č</string> <string name="alternates_for_punctuation">"\\,,\?,!,¿,¡,:,-,\',\",),(,/,;,+,&,\@"</string> - <string name="alternates_for_web_tab_punctuation">".,\\,,\?,!,¿,¡,:,-,\',\",),(,/,;,+,&,\@"</string> </resources> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 96ef88c07..c3b97c78c 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Ajustes del teclado de Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones introducción texto"</string> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Sonido al pulsar tecla"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Popup al pulsar tecla"</string> @@ -45,9 +53,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar en modo vertical"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla de ajustes"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automáticamente"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar siempre"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ocultar siempre"</string> <string name="auto_correction" msgid="4979925752001319458">"Autocorrección"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Espacio y punt para corregir errores"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivada"</string> @@ -64,6 +69,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Ok"</string> <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Más"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Espera"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro en teclado de símbolos"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entrada de voz inhabilitada"</string> <string name="selectInputMethod" msgid="315076553378705821">"Seleccionar método de introducción de texto"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de introducción"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas"</string> <string name="select_language" msgid="2573265881207142437">"Seleccionar idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Volver a tocar para guardar"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corregir palabras"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toca las palabras introducidas para corregirlas, solo cuando las sugerencias sean visibles."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema de teclado"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemán"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"inglés (Reino Unido)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"inglés (EE.UU.)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudio de usabilidad"</string> </resources> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 1ac2d41cf..5a085d878 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"صفحه کلید Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"تنظیمات صفحه کلید Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه های ورودی"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"تصحیح Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"لرزش با فشار کلید"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"صدا با فشار کلید"</string> <string name="popup_on_keypress" msgid="123894815723512944">"بازشو با فشار کلید"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"نمایش در حالت عمودی"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"همیشه پنهان شود"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"نمایش کلید تنظیمات"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"خودکار"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"همیشه نمایش"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"همیشه پنهان"</string> <string name="auto_correction" msgid="4979925752001319458">"تصحیح خودکار"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده اند تصحیح می کنند"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"خاموش"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"انجام شد"</string> <string name="label_send_key" msgid="2815056534433717444">"ارسال"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"بیشتر"</string> <string name="label_pause_key" msgid="181098308428035340">"توقف موقت"</string> <string name="label_wait_key" msgid="6402152600878093134">"منتظر بمانید"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"میکروفن در صفحه کلید نمادها"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ورودی صدا غیرفعال است"</string> <string name="selectInputMethod" msgid="315076553378705821">"انتخاب روش ورودی"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روش های ورودی"</string> <string name="language_selection_title" msgid="1651299598555326750">"زبان های ورودی"</string> <string name="select_language" msgid="2573265881207142437">"انتخاب زبان های ورودی"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← جهت ذخیره دوباره لمس کنید"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"برای تصحیح کلمات لمس کنید"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"فقط هنگامی که پیشنهادات قابل مشاهده هستند، برای تصحیح کلمات وارد شده آنها را لمس کنید"</string> <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه کلید"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"آلمانی QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"انگیسی (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"انگیسی (US)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"حالت بررسی قابلیت استفاده"</string> </resources> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 30c03cf0f..88a8ea093 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android-näppäimistö"</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> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Käytä värinää näppäimiä painettaessa"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Toista ääni näppäimiä painettaessa"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Ponnahdusikkuna painalluksella"</string> @@ -45,9 +53,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Näytä pystysuunnassa"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Piilota aina"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Näytä asetukset-näppäin"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automaattinen"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Näytä aina"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Piilota aina"</string> <string name="auto_correction" msgid="4979925752001319458">"Autom. korjaus"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Välilyönnit ja välimerkit korjaavat väärinkirjoitetut sanat automaattisesti"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Älä käytä"</string> @@ -64,6 +69,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Valmis"</string> <string name="label_send_key" msgid="2815056534433717444">"Lähetä"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Lisää"</string> <string name="label_pause_key" msgid="181098308428035340">"Tauko"</string> <string name="label_wait_key" msgid="6402152600878093134">"Odota"</string> @@ -131,8 +139,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. symbolinäppäim."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Ääniohjaus on pois käytöstä"</string> <string name="selectInputMethod" msgid="315076553378705821">"Valitse syöttötapa"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Valitse syöttökielet"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tallenna koskettamalla uudelleen"</string> @@ -142,11 +149,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Korjaa sanoja koskettamalla"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Korjaa annetut sanat napauttamalla. (Vain, kun ehdotuksia on näkyvillä.)"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Näppäimistöteema"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"saksa, QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"englanti (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"englanti (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Käytettävyystutkimustila"</string> </resources> diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml index 09c37e31a..695750f32 100644 --- a/java/res/values-fr/donottranslate.xml +++ b/java/res/values-fr/donottranslate.xml @@ -25,5 +25,5 @@ <!-- Symbols that should promote magic spaces into real space --> <string name="magic_space_promoting_symbols">;:!?([*&@{<>+=|</string> <!-- Symbols that do NOT separate words --> - <string name="non_word_separator_symbols">\u0027</string> + <string name="symbols_excluded_from_word_separators">\u0027</string> </resources> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index bca7e78d5..c35c9bb17 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Correcteur Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Afficher en mode Portrait"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Toujours masquer"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Afficher touche param."</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatique"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Toujours afficher"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Toujours masquer"</string> <string name="auto_correction" msgid="4979925752001319458">"Correction auto."</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Corriger autom. orthographe (pression sur barre espace/signes ponctuation)"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Désactiver"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"OK"</string> <string name="label_send_key" msgid="2815056534433717444">"Envoyer"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Plus"</string> <string name="label_pause_key" msgid="181098308428035340">"Pause"</string> <string name="label_wait_key" msgid="6402152600878093134">"Attente"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro sur clavier symboles"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Saisie vocale désactivée"</string> <string name="selectInputMethod" msgid="315076553378705821">"Sélectionner un mode de saisie."</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Sélectionner les langues de saisie"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Appuyer de nouveau pour enregistrer"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Appuyer pour corriger"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Appuyer sur les mots saisis pour les corriger, uniquement lorsque des suggestions sont visibles"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Thème du clavier"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Clavier QWERTY allemand"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (Royaume-Uni)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'étude de l\'utilisabilité"</string> </resources> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index a4d8a65fa..4d27d2c9a 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android tipkovnica"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Postavke tipkovnice za Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Ispravak za Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibracija pri pritisku na tipku"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk pri pritisku tipke"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Povećanja na pritisak tipke"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Prikaži u portretnom načinu"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Uvijek sakrij"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Prikaži tipku postavki"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatski"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Uvijek prikaži"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Uvijek sakrij"</string> <string name="auto_correction" msgid="4979925752001319458">"Samoispravak"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Razm. i intrp. aut. ispr. kr. rči."</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Isključeno"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Gotovo"</string> <string name="label_send_key" msgid="2815056534433717444">"Pošalji"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Više"</string> <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string> <string name="label_wait_key" msgid="6402152600878093134">"Pričekaj"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. na tipk. simb."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. unos onemog."</string> <string name="selectInputMethod" msgid="315076553378705821">"Odabir ulazne metode"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfiguriraj načine ulaza"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jezici unosa"</string> <string name="select_language" msgid="2573265881207142437">"Odaberite jezike unosa"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Dodirnite opet za spremanje"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dodirnite za ispravak riječi"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dodirnite unesene riječi da biste ih ispravili samo kada su prijedlozi vidljivi"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema tipkovnice"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"njemački QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Engleski (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Engleski (SAD)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način studije upotrebljivosti"</string> </resources> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index f3cbc3130..afae17d91 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android-billentyűzet"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android billentyűzetbeállítások"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android korrekció"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rezgés billentyű megnyomása esetén"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Hangjelzés billentyű megnyomása esetén"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Legyen nagyobb billentyű lenyomásakor"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Megjelenítés álló tájolásban"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Mindig rejtve"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Beállítások billentyű megjelenítése"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatikus"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mindig látszik"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Mindig rejtve"</string> <string name="auto_correction" msgid="4979925752001319458">"Automatikus javítás"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Szóköz és központozás automatikusan javítja az elgépelést"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Ki"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Kész"</string> <string name="label_send_key" msgid="2815056534433717444">"Küldés"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Egyebek"</string> <string name="label_pause_key" msgid="181098308428035340">"Szün."</string> <string name="label_wait_key" msgid="6402152600878093134">"Vár"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. a szimbólumoknál"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hangbevivel KI"</string> <string name="selectInputMethod" msgid="315076553378705821">"Beviteli mód kiválasztása"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Válassza ki a beviteli nyelveket"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Érintse meg újra a mentéshez"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Javítás a szavak megérintésével"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"A beírt szavakat csak akkor javíthatja ki megérintve, ha látszanak javaslatok"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Billentyűzettéma"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Német QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"angol (Egyesült Királyság)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"angol (Egyesült Államok)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Használhatósági teszt"</string> </resources> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index e47fe376d..f3506863f 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Keyboard Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Setelan keyboard Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Koreksi android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar jika tombol ditekan"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Berbunyi jika tombol ditekan"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Muncul saat tombol ditekan"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Tampilkan pada mode potret"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Selalu sembunyikan"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Lihat tombol setelan"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Otomatis"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Selalu tampilkan"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Selalu sembunyikan"</string> <string name="auto_correction" msgid="4979925752001319458">"Koreksi otomatis"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Bilah spasi dan tanda baca secara otomatis dikoreksi pada kata yang salah ketik"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Mati"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Selesai"</string> <string name="label_send_key" msgid="2815056534433717444">"Kirimkan"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Lainnya"</string> <string name="label_pause_key" msgid="181098308428035340">"Jeda"</string> <string name="label_wait_key" msgid="6402152600878093134">"Tunggu"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik pada keyboard simbol"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Masukan suara dinonaktifkan"</string> <string name="selectInputMethod" msgid="315076553378705821">"Pilih metode masukan"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurasi metode masukan"</string> <string name="language_selection_title" msgid="1651299598555326750">"Bahasa masukan"</string> <string name="select_language" msgid="2573265881207142437">"Pilih bahasa masukan"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Sentuh sekali lagi untuk menyimpan"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Sentuh untuk memperbaiki kata"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Sentuh kata yang dimasukkan untuk memperbaikinya, hanya saat saran dapat dilihat"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema keyboard"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY Jerman"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Inggris (Inggris)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Inggris (AS)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus studi daya guna"</string> </resources> diff --git a/java/res/values-it/donottranslate.xml b/java/res/values-it/donottranslate.xml index adb2a9a9d..58e94361b 100644 --- a/java/res/values-it/donottranslate.xml +++ b/java/res/values-it/donottranslate.xml @@ -19,5 +19,5 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Symbols that do NOT separate words --> - <string name="non_word_separator_symbols"></string> + <string name="symbols_excluded_from_word_separators"></string> </resources> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 72e2653f4..f67c3e474 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Tastiera Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Correzione Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Suono tasti"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Popup sui tasti"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostra in modalità verticale"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Nascondi sempre"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Mostra tasto impostaz."</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatico"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostra sempre"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Nascondi sempre"</string> <string name="auto_correction" msgid="4979925752001319458">"Correzione automatica"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Barra spaziatrice/punteggiatura correggono parole con errori"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Fine"</string> <string name="label_send_key" msgid="2815056534433717444">"Invia"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Altro"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Attesa"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic su tastiera simboli"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Comandi vocali disatt."</string> <string name="selectInputMethod" msgid="315076553378705821">"Seleziona metodo di inserimento"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Configura metodi di immissione"</string> <string name="language_selection_title" msgid="1651299598555326750">"Lingue comandi"</string> <string name="select_language" msgid="2573265881207142437">"Seleziona lingue di immissione"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tocca di nuovo per salvare"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocca per correggere"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tocca le parole inserite per correggerle, solo quando sono visibili i suggerimenti"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema della tastiera"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY tedesca"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Inglese (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Inglese (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modalità Studio sull\'usabilità"</string> </resources> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index e431cd0a0..31632e9b0 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"מקלדת Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"הגדרות מקלדת של Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"תיקון Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"רטט עם לחיצה על מקשים"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"צלילים עם לחיצה על מקשים"</string> <string name="popup_on_keypress" msgid="123894815723512944">"חלון קופץ עם לחיצה על מקשים"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"הצג בפריסה לאורך"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"הסתר תמיד"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"הצג מקש הגדרות"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"אוטומטי"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"הצג תמיד"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"הסתר תמיד"</string> <string name="auto_correction" msgid="4979925752001319458">"תיקון אוטומטי"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"מקש הרווח ופיסוק מתקנים אוטומטית שגיאות הקלדה"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"כבוי"</string> @@ -63,7 +67,9 @@ <string name="label_next_key" msgid="362972844525672568">"הבא"</string> <string name="label_done_key" msgid="2441578748772529288">"בוצע"</string> <string name="label_send_key" msgid="2815056534433717444">"שלח"</string> - <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_alpha_key" msgid="4793983863798817523">"אבג"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"עוד"</string> <string name="label_pause_key" msgid="181098308428035340">"השהה"</string> <string name="label_wait_key" msgid="6402152600878093134">"המתן"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"מיקרופון במקלדת הסמלים"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"הקלט הקולי מושבת"</string> <string name="selectInputMethod" msgid="315076553378705821">"בחר שיטת קלט"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"הגדרת שיטות קלט"</string> <string name="language_selection_title" msgid="1651299598555326750">"שפות קלט"</string> <string name="select_language" msgid="2573265881207142437">"בחר שפות קלט"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← גע שוב כדי לשמור"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"גע כדי לתקן מילים"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"גע במילים שהוזנו כדי לתקן אותן, רק כאשר הצעות מוצגות"</string> <string name="keyboard_layout" msgid="8451164783510487501">"עיצוב מקלדת"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"מקלדת QWERTY גרמנית"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"אנגלית (בריטניה)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"אנגלית (ארה\"ב)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"מצב מחקר שימושיות"</string> </resources> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index cc6f000dd..08b572203 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Androidキーボード"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボードの設定"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android校正"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"キー操作音"</string> <string name="popup_on_keypress" msgid="123894815723512944">"キー押下時ポップアップ"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"縦向きで表示"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"常に非表示"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"設定キーを表示"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"自動"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"常に表示"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"常に非表示"</string> <string name="auto_correction" msgid="4979925752001319458">"自動修正"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"誤入力をスペースまたは句読点キーで修正する"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"OFF"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"完了"</string> <string name="label_send_key" msgid="2815056534433717444">"送信"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Shift"</string> <string name="label_pause_key" msgid="181098308428035340">"停止"</string> <string name="label_wait_key" msgid="6402152600878093134">"待機"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"記号キーボードのマイク"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"音声入力は無効です"</string> <string name="selectInputMethod" msgid="315076553378705821">"入力方法の選択"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"入力方法を設定"</string> <string name="language_selection_title" msgid="1651299598555326750">"入力言語"</string> <string name="select_language" msgid="2573265881207142437">"入力言語の選択"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"←保存するにはもう一度タップ"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"タップして語句を修正"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"候補が表示されているときのみ、入力した語句をタップして修正する"</string> <string name="keyboard_layout" msgid="8451164783510487501">"キーボードのテーマ"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"ドイツ語QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"英語(英国)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"英語(米国)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"使いやすさの研究モード"</string> </resources> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 32d8e5a1c..83cf906c8 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android 키보드"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android 키보드 설정"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android 수정"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"키를 누를 때 소리 발생"</string> <string name="popup_on_keypress" msgid="123894815723512944">"키를 누를 때 팝업"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"세로 모드로 표시"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"항상 숨기기"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"설정 키 표시"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"자동"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"항상 표시"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"항상 숨기기"</string> <string name="auto_correction" msgid="4979925752001319458">"자동 수정"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"스페이스바와 문장부호 키를 사용하면 오타가 자동으로 교정됩니다."</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"사용 안함"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"완료"</string> <string name="label_send_key" msgid="2815056534433717444">"전송"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"더보기"</string> <string name="label_pause_key" msgid="181098308428035340">"일시 중지"</string> <string name="label_wait_key" msgid="6402152600878093134">"대기"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"기호 키보드의 마이크"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"음성 입력이 사용 중지됨"</string> <string name="selectInputMethod" msgid="315076553378705821">"입력 방법 선택"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"입력 방법 설정"</string> <string name="language_selection_title" msgid="1651299598555326750">"입력 언어"</string> <string name="select_language" msgid="2573265881207142437">"입력 언어 선택"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← 저장하려면 다시 터치하세요."</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"터치하여 단어 수정"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"입력한 단어를 터치하여 수정(추천 단어가 표시되는 경우에만)"</string> <string name="keyboard_layout" msgid="8451164783510487501">"키보드 테마"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"독일어 QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"영어(영국)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"영어(미국)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"가용성 연구 모드"</string> </resources> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 15f9c0e84..24073f766 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"„Android“ klaviatūra"</string> <string name="english_ime_settings" msgid="6661589557206947774">"„Android“ klaviatūros nustatymai"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"„Android“ korekcijos"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibruoti, kai paspaudžiami klavišai"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Klavišo paspaudimo garsas"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Iššoka paspaudus klavišą"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Rodyti stačiuoju režimu"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Visada slėpti"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Rodyti nustatymų raktą"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatinis"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Visada rodyti"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Visada slėpti"</string> <string name="auto_correction" msgid="4979925752001319458">"Automatinis taisymas"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Tarpo kl. ir skyr. ženkl. aut. išt. neteis. įv. žodž."</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Išjungta"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Atlikta"</string> <string name="label_send_key" msgid="2815056534433717444">"Siųsti"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Daugiau"</string> <string name="label_pause_key" msgid="181098308428035340">"Prist."</string> <string name="label_wait_key" msgid="6402152600878093134">"Lauk."</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrof. simb. klav."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balso įv. neleidž."</string> <string name="selectInputMethod" msgid="315076553378705821">"Pasirinkti įvesties metodą"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigūruoti įvesties metodus"</string> <string name="language_selection_title" msgid="1651299598555326750">"Įvesties kalbos"</string> <string name="select_language" msgid="2573265881207142437">"Pasirinkite įvesties kalbas"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Kad išsaugotumėte, dar kartą palieskite"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Jei norite ištais. žodž., paliesk."</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Jei norite ištaisyti įvestus žodžius, palieskite juos tik tada, kai matomi pasiūlymai"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatūros tema"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Vokiška QWERTY klaviatūra"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Anglų (JK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Anglų k. (JAV)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tinkamumo tyrimo režimas"</string> </resources> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 78728ea13..2ec111a35 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android tastatūra"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android tastatūras iestatījumi"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android korekcija"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrēt, nospiežot taustiņu"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Skaņa, nospiežot taustiņu"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Nospiežot taustiņu, parādīt uznirstošo izvēlni"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Rādīt portreta režīmā"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vienmēr slēpt"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Rādīt iestatījumu taustiņu"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automātiski"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vienmēr rādīt"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vienmēr slēpt"</string> <string name="auto_correction" msgid="4979925752001319458">"Automāt. korekcija"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Atstarpes taustiņš un interpunkcija; automātiska kļūdainu vārdu labošana"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izslēgta"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Gatavs"</string> <string name="label_send_key" msgid="2815056534433717444">"Sūtīt"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Vairāk"</string> <string name="label_pause_key" msgid="181098308428035340">"Pauze"</string> <string name="label_wait_key" msgid="6402152600878093134">"Gaidīt"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr.uz simb.tastat."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balss iev. atspējota"</string> <string name="selectInputMethod" msgid="315076553378705821">"Atlasīt ievades metodi"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Atlasīt ievades valodas"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Pieskarieties vēlreiz, lai saglabātu"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Pieskarties, lai izlabotu vārdus"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Pieskarties ievadītajiem vārdiem, lai tos labotu (tikai tad, ja tiek rādīti ieteikumi)."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastatūras motīvs"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Vācu valodas QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Angļu valoda (Lielbritānija)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Angļu valoda (ASV)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Lietojamības izpētes režīms"</string> </resources> diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml index 907cddb0a..eef6ef2e2 100644 --- a/java/res/values-ms/strings.xml +++ b/java/res/values-ms/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Papan kekunci Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Tetapan papan kekunci Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Pembetulan Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar pada tekanan kekunci"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Bunyi pada tekanan kekunci"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Pop timbul pada tekanan kunci"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Tunjukkan pada mod potret"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sentiasa sembunyikan"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Tunjukkan kekunci tetapan"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatik"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Sentiasa tunjukkan"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Sentiasa sembunyikan"</string> <string name="auto_correction" msgid="4979925752001319458">"Auto Pembetulan"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Bar ruang dan tanda baca secara automatik membetulkan perkataan yang ditaip salah"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Matikan"</string> @@ -64,6 +68,10 @@ <string name="label_done_key" msgid="2441578748772529288">"Selesai"</string> <string name="label_send_key" msgid="2815056534433717444">"Hantar"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <!-- no translation found for label_to_symbol_key (8516904117128967293) --> + <skip /> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Lagi"</string> <string name="label_pause_key" msgid="181098308428035340">"Jeda"</string> <string name="label_wait_key" msgid="6402152600878093134">"Tnggu"</string> @@ -131,8 +139,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. pd kekunci smbl"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Input suara dilmphkn"</string> <string name="selectInputMethod" msgid="315076553378705821">"Pilih kaedah input"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurasikan kaedah input"</string> <string name="language_selection_title" msgid="1651299598555326750">"Bahasa input"</string> <string name="select_language" msgid="2573265881207142437">"Pilih bahasa input"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Sentuh sekali lagi untuk menyimpan"</string> @@ -142,11 +149,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Sentuh untuk membetulkan perkataan"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Sentuh perkataan yang dimasukkan untuk membetulkannya, hanya apabila cadangan boleh dilihat"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema papan kekunci"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY Bahasa Jerman"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Bahasa Inggeris (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Bahasa Inggeris (AS)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mod kajian kebolehgunaan"</string> </resources> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 8e188ce60..62140d3e8 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Skjermtastatur"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android-stavekontroll"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetrykk"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Hurtigvindu ved tastetrykk"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Vis i stående modus"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul alltid"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Vis innstillingsnøkkel"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisk"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vis alltid"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Skjul alltid"</string> <string name="auto_correction" msgid="4979925752001319458">"Automatisk retting"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Mellomromstast og skilletegn retter automat. feilstavede ord"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Utfør"</string> <string name="label_send_key" msgid="2815056534433717444">"Send"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Mer"</string> <string name="label_pause_key" msgid="181098308428035340">"Pause"</string> <string name="label_wait_key" msgid="6402152600878093134">"Vent"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon på talltastatur"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Taleinndata er deaktiv."</string> <string name="selectInputMethod" msgid="315076553378705821">"Velg inndatametode"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inndatametoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inndataspråk"</string> <string name="select_language" msgid="2573265881207142437">"Velg inndataspråk"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"Trykk på nytt for å lagre"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Trykk for å endre ord"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Når forslag er synlige, kan du trykke på ord du har skrevet inn, for å endre dem"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturtema"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tysk QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannia)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Nyttighetsmodus"</string> </resources> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index 9c267cb6a..26e138d55 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android-spellingcontrole"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij toetsaanslag"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Geluid bij toetsaanslag"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bij toetsaanslag"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Weergeven in staande modus"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Altijd verbergen"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Instellingscode weergeven"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatisch"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Altijd weergeven"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Altijd verbergen"</string> <string name="auto_correction" msgid="4979925752001319458">"Autocorrectie"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Met spatiebalk en interpunctie worden verkeerd gespelde woorden automatisch gecorrigeerd"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Uitgeschakeld"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Gereed"</string> <string name="label_send_key" msgid="2815056534433717444">"Verzenden"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Meer"</string> <string name="label_pause_key" msgid="181098308428035340">"Onderbr."</string> <string name="label_wait_key" msgid="6402152600878093134">"Wacht"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic op symb.toetsb."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spraakinvoer is uit"</string> <string name="selectInputMethod" msgid="315076553378705821">"Invoermethode selecteren"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Invoermethoden configureren"</string> <string name="language_selection_title" msgid="1651299598555326750">"Invoertalen"</string> <string name="select_language" msgid="2573265881207142437">"Invoertalen selecteren"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Raak nogmaals aan om op te slaan"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Raak aan om woorden te corrigeren"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Ingevoerde woorden aanraken om ze te verbeteren, alleen mogelijk wanneer suggesties zichtbaar zijn"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Toetsenbordthema"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Duits QWERTY-toetsenbord"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Engels (Verenigd Koninkrijk)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Engels (Verenigde Staten)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus voor gebruiksvriendelijkheidsonderzoek"</string> </resources> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index 616118bc7..72687b58d 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Klawiatura Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Korekta Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Dźwięk przy naciśnięciu"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Powiększ po naciśnięciu"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Pokaż w trybie pionowym"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Zawsze ukrywaj"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Pokaż klawisz ustawień"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatycznie"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Zawsze pokazuj"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Zawsze ukrywaj"</string> <string name="auto_correction" msgid="4979925752001319458">"Autokorekta"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Spacja i znaki przestankowe poprawiają błędnie wpisane słowa"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Wyłącz"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"OK"</string> <string name="label_send_key" msgid="2815056534433717444">"Wyślij"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Więcej"</string> <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string> <string name="label_wait_key" msgid="6402152600878093134">"Czekaj"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon na klawiaturze z symbolami"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Wprowadzanie głosowe jest wyłączone"</string> <string name="selectInputMethod" msgid="315076553378705821">"Wybierz sposób wprowadzania tekstu"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurowanie metod wprowadzania"</string> <string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string> <string name="select_language" msgid="2573265881207142437">"Wybierz języki wprowadzania"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Dotknij ponownie, aby zapisać"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Popraw dotknięte słowo"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dotykaj wprowadzonych słów, aby je poprawiać tylko wówczas, gdy widoczne są sugestie."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Motyw klawiatury"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Niemiecka QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Angielska (Wielka Brytania)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Angielska (USA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tryb badania przydatności"</string> </resources> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 7735b7b01..7d1b6d0fa 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Teclado do Android"</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> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao premir as teclas"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Mostrar popup ao premir tecla"</string> @@ -45,9 +53,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar no modo de retrato"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar sempre"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla das definições"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automático"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar sempre"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ocultar sempre"</string> <string name="auto_correction" msgid="4979925752001319458">"Auto correcção"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Correcção automática de palavras mal escritas c/ barra de espaços e pontuação"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desligar"</string> @@ -64,6 +69,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Feito"</string> <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Mais"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Esp."</string> @@ -131,8 +139,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic. tecl. símbolos"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. voz desact."</string> <string name="selectInputMethod" msgid="315076553378705821">"Seleccionar método de entrada"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Selecionar idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Toque novamente para guardar"</string> @@ -142,11 +149,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corrigir palavras"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tocar nas palavras introduzidas para as corrigir, apenas quando as sugestões estiverem visíveis"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema do teclado"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY Alemão"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Inglês (RU)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Inglês (EUA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo da capacidade de utilização"</string> </resources> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index 935cbb9a2..793c78fd4 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Teclado Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Correção do Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao tocar a tecla"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Exibir pop-up ao digitar"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar em modo retrato"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sempre ocultar"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Mostrar tecla de config."</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automático"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Mostrar sempre"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Sempre ocultar"</string> <string name="auto_correction" msgid="4979925752001319458">"Autocorreção"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"A barra de espaço e a pontuação corrigem automaticamente palavras com erro de digitação"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desativado"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Feito"</string> <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Mais"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Esp."</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic. no teclado"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Texto por voz desat."</string> <string name="selectInputMethod" msgid="315076553378705821">"Selecionar método de entrada"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Selecionar idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Toque novamente para salvar"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tocar para corrigir"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Toque nas palavras digitadas para corrigi-las apenas quando as sugestões estiverem visíveis"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema do teclado"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY alemão"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Inglês (Reino Unido)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Inglês (EUA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo de utilização"</string> </resources> diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index 35ba3764b..9901b9f66 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -24,6 +24,14 @@ <string name="english_ime_settings" msgid="6661589557206947774">"Parameters da la tastatura Android"</string> <!-- no translation found for english_ime_input_options (3909945612939668554) --> <skip /> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar cun smatgar in buttun"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Tun cun smatgar in buttun"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up cun smatgar ina tasta"</string> @@ -64,12 +72,6 @@ <skip /> <!-- no translation found for prefs_settings_key (4623341240804046498) --> <skip /> - <!-- no translation found for settings_key_mode_auto_name (2993460277873684680) --> - <skip /> - <!-- no translation found for settings_key_mode_always_show_name (3047567041784760575) --> - <skip /> - <!-- no translation found for settings_key_mode_always_hide_name (7833948046716923994) --> - <skip /> <!-- outdated translation 7911639788808958255 --> <string name="auto_correction" msgid="4979925752001319458">"Propostas da pleds"</string> <!-- outdated translation 6881047311475758267 --> <string name="auto_correction_summary" msgid="5625751551134658006">"Curreger automaticamain il pled precedent"</string> <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) --> @@ -93,6 +95,10 @@ <string name="label_send_key" msgid="2815056534433717444">"Trametter"</string> <!-- no translation found for label_to_alpha_key (4793983863798817523) --> <skip /> + <!-- no translation found for label_to_symbol_key (8516904117128967293) --> + <skip /> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <!-- no translation found for label_more_key (3760239494604948502) --> <skip /> <!-- no translation found for label_pause_key (181098308428035340) --> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 9a048a8ff..1dd083e55 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Tastatură Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Setările tastaturii Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Corecţie Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrare la apăsarea tastei"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Sunet la apăsarea tastei"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Fereastră pop-up la apăsarea tastei"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Afişaţi în modul Portret"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ascundeţi întotdeauna"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Afişaţi tasta setări"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automat"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Afişaţi întotdeauna"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Ascundeţi întotdeauna"</string> <string name="auto_correction" msgid="4979925752001319458">"Autocorecţie"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Corectare automată cuvinte prin bară spaţiu/semne punctuaţie"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Dezactivată"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Terminat"</string> <string name="label_send_key" msgid="2815056534433717444">"Trimiteţi"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Mai multe"</string> <string name="label_pause_key" msgid="181098308428035340">"Pauză"</string> <string name="label_wait_key" msgid="6402152600878093134">"Aşt."</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micr. pe tast. simb."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Intr. vocală dezact."</string> <string name="selectInputMethod" msgid="315076553378705821">"Selectaţi metoda de introducere a textului"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Selectaţi limbile de intrare"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Atingeţi din nou pentru a salva"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Atingeţi pentru a corecta cuvintele"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Atingeţi cuvintele introduse pentru a le corecta, numai când pot fi văzute sugestii"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Temă pentru tastatură"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tastatură germană QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Engleză (Marea Britanie)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Engleză (S.U.A.)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modul Studiu privind utilizarea"</string> </resources> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 509348bc3..64c2aa60b 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Настройки клавиатуры Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ввода"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Исправления Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Звук клавиш"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Увеличение нажатых"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показать вертикально"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Всегда скрывать"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Кнопка настроек"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Автоматически"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Всегда показывать"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Всегда скрывать"</string> <string name="auto_correction" msgid="4979925752001319458">"Автоисправление"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Автоматическое исправление опечаток при вводе знака препинания или пробела"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Откл."</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Готово"</string> <string name="label_send_key" msgid="2815056534433717444">"Отправить"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"АБВ"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Ещё"</string> <string name="label_pause_key" msgid="181098308428035340">"Приостановить"</string> <string name="label_wait_key" msgid="6402152600878093134">"Подождите"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Значок на клавиатуре символов"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голосовой ввод откл."</string> <string name="selectInputMethod" msgid="315076553378705821">"Выбрать способ ввода"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Настройка способов ввода"</string> <string name="language_selection_title" msgid="1651299598555326750">"Языки ввода"</string> <string name="select_language" msgid="2573265881207142437">"Выберите языки ввода"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Нажмите еще раз, чтобы сохранить"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Исправление нажатием"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Нажмите на слово, чтобы исправить его (при наличии подсказок)"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавиатуры"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Немецкая клавиатура QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Английский (Великобритания)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Английский (США)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим проверки удобства использования"</string> </resources> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 1486e0136..3e7ed1475 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Klávesnica Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Nastavenia klávesnice Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Opravy pravopisu Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Pri stlačení klávesu vibrovať"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk pri stlačení klávesu"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Zobraziť znaky pri stlačení klávesu"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Zobraziť v režime na výšku"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývať"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Zobraziť kláves Nastavenia"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automaticky"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vždy zobrazovať"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vždy skrývať"</string> <string name="auto_correction" msgid="4979925752001319458">"Automatické opravy"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Stlačením medzerníka a interpunkcie sa aut. opravia chybné slová"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuté"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Hotovo"</string> <string name="label_send_key" msgid="2815056534433717444">"Odoslať"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Viac"</string> <string name="label_pause_key" msgid="181098308428035340">"Pozastaviť"</string> <string name="label_wait_key" msgid="6402152600878093134">"Čakajte"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofón na klávesnici so symbolmi"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup je zakázaný"</string> <string name="selectInputMethod" msgid="315076553378705821">"Výber metódy vstupu"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurovať metódy vstupu"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jazyky vstupu"</string> <string name="select_language" msgid="2573265881207142437">"Vybrať jazyky vstupu"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Ďalším dotykom slovo uložíte"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dotykom opravíte slová"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dotykom zadaných slov tieto slová opravíte, musia však byť viditeľné návrhy"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Motív klávesnice"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Nemecká klávesnica QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Anglická klávesnica (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Anglická klávesnica (US)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim štúdie použiteľnosti"</string> </resources> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 6fcd82aa7..84498ad19 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Tipkovnica Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Nastavitve tipkovnice Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Popravek za Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibriranje ob pritisku tipke"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Zvok ob pritisku tipke"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Pojavno okno ob pritisku tipke"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Pokaži v pokončnem načinu"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vedno skrij"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Pokaži tipko za nastavitve"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Samodejno"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Vedno pokaži"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Vedno skrij"</string> <string name="auto_correction" msgid="4979925752001319458">"Samodejni popravek"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Preslednica in ločila samodejno popravijo napačno vtipkane besede"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izklopljeno"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Dokončano"</string> <string name="label_send_key" msgid="2815056534433717444">"Pošlji"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Več"</string> <string name="label_pause_key" msgid="181098308428035340">"Premor"</string> <string name="label_wait_key" msgid="6402152600878093134">"Čakaj"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. na tipk. s sim."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. vnos je onem."</string> <string name="selectInputMethod" msgid="315076553378705821">"Izberite način vnosa"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Nastavitev načinov vnosa"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jeziki vnosa"</string> <string name="select_language" msgid="2573265881207142437">"Izberite jezike vnosa"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Še enkrat se dotaknite, da shranite"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Dotaknite se besed in jih popravite"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Dotaknite se vnesenih besed in jih popravite, samo ko so predlogi vidni"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema tipkovnice"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Nemška QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"angleščina (Združeno kraljestvo)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"angleščina (ZDA)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način za preučevanje uporabnosti"</string> </resources> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 74c6e3e38..d465aaedc 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android тастатура"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Подешавања Android тастатуре"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android исправљање"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вибрирај на притисак тастера"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Звук на притисак тастера"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Искачући прозор приликом притиска тастера"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Прикажи у усправном режиму"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Увек сакриј"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Прикажи тастер за подешавања"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Аутоматски"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Увек прикажи"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Увек сакриј"</string> <string name="auto_correction" msgid="4979925752001319458">"Аутоматско исправљање"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Размак и интерпункција аутоматски исправљају грешке у куцању"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Искључи"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Готово"</string> <string name="label_send_key" msgid="2815056534433717444">"Пошаљи"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Још"</string> <string name="label_pause_key" msgid="181098308428035340">"Паузирај"</string> <string name="label_wait_key" msgid="6402152600878093134">"Сачекајте"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Микрофон на тастатури са симболима"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Гласовни унос је онемогућен"</string> <string name="selectInputMethod" msgid="315076553378705821">"Изаберите метод уноса"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Конфигурисање метода уноса"</string> <string name="language_selection_title" msgid="1651299598555326750">"Језици за унос"</string> <string name="select_language" msgid="2573265881207142437">"Изабери језике за унос"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Поново додирните да бисте сачували"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Додирните да бисте исправили речи"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Додирните унете речи да бисте их исправили само када су предлози видљиви"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема тастатуре"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY тастатура за немачки"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"енглески (УК)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"енглески (САД)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за студију могућности коришћења"</string> </resources> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index c259ab9b4..61a7f1362 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Androids tangentbord"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android-korrigering"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Knappljud"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Popup vid knapptryck"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Visa stående"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Dölj alltid"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Visa inställningsknapp"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Automatiskt"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Visa alltid"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Dölj alltid"</string> <string name="auto_correction" msgid="4979925752001319458">"Autokorrigering"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Blanksteg/skiljetecken rättar felstavning"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Färdig"</string> <string name="label_send_key" msgid="2815056534433717444">"Skicka"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Mer"</string> <string name="label_pause_key" msgid="181098308428035340">"Pausa"</string> <string name="label_wait_key" msgid="6402152600878093134">"Vänta"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mick bland symboler"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Röstinmatning inaktiv"</string> <string name="selectInputMethod" msgid="315076553378705821">"Välj inmatningsmetod"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Konfigurera inmatningsmetoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inmatningsspråk"</string> <string name="select_language" msgid="2573265881207142437">"Välj inmatningsspråk"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Tryck igen för att spara"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Tryck om du vill korrigera ord"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Tryck på skrivna ord om du vill korrigera dem, endast när förslag visas"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tangentbordstema"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Tyskt QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Engelskt (brittiskt)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Engelskt (amerikanskt)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Läge för studie av användbarhet"</string> </resources> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 7d469dabd..9c677d177 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Kibodi ya Android"</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> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tetema unabofya kitufe"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Toa sauti unapobofya kitufe"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Ibuka kitufe kinapobonyezwa"</string> @@ -58,9 +66,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Onyesha kwenye hali wima"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ficha kila wakati"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Onyesha kitufe cha mipangilio"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Kiotomatiki"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Onyesha kila wakati"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">" Ficha kila mara"</string> <string name="auto_correction" msgid="4979925752001319458">"Usahihishaji Kioto"</string> <!-- outdated translation 6881047311475758267 --> <string name="auto_correction_summary" msgid="5625751551134658006">"Sahihisha neno lililotangulia kiotomatiki"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Zima"</string> @@ -80,6 +85,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Kwisha"</string> <string name="label_send_key" msgid="2815056534433717444">"Tuma"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <!-- no translation found for label_to_symbol_key (8516904117128967293) --> + <skip /> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Zaidi"</string> <string name="label_pause_key" msgid="181098308428035340">"Pumzisha"</string> <string name="label_wait_key" msgid="6402152600878093134">"Subiri"</string> diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml index 5d4967ffe..c75fcc0d1 100644 --- a/java/res/values-sw600dp-land/dimens.xml +++ b/java/res/values-sw600dp-land/dimens.xml @@ -51,5 +51,4 @@ <fraction name="key_uppercase_letter_ratio">29%</fraction> <dimen name="candidate_strip_padding">40.0mm</dimen> - <integer name="candidate_count_in_strip">5</integer> </resources> diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml index 4c73f3912..d539e0d82 100644 --- a/java/res/values-sw600dp/config.xml +++ b/java/res/values-sw600dp/config.xml @@ -20,9 +20,9 @@ <resources> <bool name="config_enable_show_settings_key_option">true</bool> + <bool name="config_default_show_settings_key">false</bool> <bool name="config_enable_show_voice_key_option">false</bool> <bool name="config_enable_show_popup_on_keypress_option">false</bool> - <bool name="config_enable_show_recorrection_option">false</bool> <bool name="config_enable_bigram_suggestions_option">false</bool> <bool name="config_sliding_key_input_enabled">false</bool> <bool name="config_digit_popup_characters_enabled">false</bool> diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml index 18837fecc..0cfdffddd 100644 --- a/java/res/values-sw768dp-land/dimens.xml +++ b/java/res/values-sw768dp-land/dimens.xml @@ -59,5 +59,4 @@ <dimen name="key_preview_offset_ics">0.05in</dimen> <dimen name="candidate_strip_padding">40.0mm</dimen> - <integer name="candidate_count_in_strip">5</integer> </resources> diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml index 2dcf7b8fb..0f8f106b7 100644 --- a/java/res/values-sw768dp/config.xml +++ b/java/res/values-sw768dp/config.xml @@ -20,9 +20,9 @@ <resources> <bool name="config_enable_show_settings_key_option">false</bool> + <bool name="config_default_show_settings_key">true</bool> <bool name="config_enable_show_voice_key_option">false</bool> <bool name="config_enable_show_popup_on_keypress_option">false</bool> - <bool name="config_enable_show_recorrection_option">false</bool> <bool name="config_enable_bigram_suggestions_option">false</bool> <bool name="config_sliding_key_input_enabled">false</bool> <bool name="config_digit_popup_characters_enabled">false</bool> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index b0e21d1e0..c53ed2e65 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"แป้นพิมพ์ Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"การตั้งค่าแป้นพิมพ์ Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"การแก้ไขของ Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"สั่นเมื่อกดปุ่ม"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"ส่งเสียงเมื่อกดปุ่ม"</string> <string name="popup_on_keypress" msgid="123894815723512944">"ป๊อปอัปเมื่อกดแป้น"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"แสดงในโหมดแนวตั้ง"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ซ่อนทุกครั้ง"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"แสดงแป้นการตั้งค่า"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"อัตโนมัติ"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"แสดงตลอดเวลา"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"ซ่อนตลอดเวลา"</string> <string name="auto_correction" msgid="4979925752001319458">"การแก้ไขอัตโนมัติ"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"กดเว้นวรรคและเครื่องหมายจะแก้คำผิดอัตโนมัติ"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ปิด"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"เสร็จสิ้น"</string> <string name="label_send_key" msgid="2815056534433717444">"ส่ง"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"เพิ่มเติม"</string> <string name="label_pause_key" msgid="181098308428035340">"หยุดชั่วคราว"</string> <string name="label_wait_key" msgid="6402152600878093134">"รอ"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ไมค์บนแป้นพิมพ์สัญลักษณ์"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ปิดใช้งานป้อนข้อมูลด้วยเสียง"</string> <string name="selectInputMethod" msgid="315076553378705821">"เลือกวิธีการป้อนข้อมูล"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"กำหนดค่าวิธีการป้อนข้อมูล"</string> <string name="language_selection_title" msgid="1651299598555326750">"ภาษาในการป้อนข้อมูล"</string> <string name="select_language" msgid="2573265881207142437">"เลือกภาษาสำหรับการป้อนข้อมูล"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← แตะอีกครั้งเพื่อบันทึก"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"แตะเพื่อแก้ไขคำ"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"แตะคำที่ป้อนไว้เพื่อแก้ไข เฉพาะเมื่อเห็นข้อเสนอแนะเท่านั้น"</string> <string name="keyboard_layout" msgid="8451164783510487501">"ชุดรูปแบบแป้นพิมพ์"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"QWERTY ภาษาเยอรมัน"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"อังกฤษ (สหราชอาณาจักร)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"อังกฤษ (สหรัฐอเมริกา)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"โหมดศึกษาประโยชน์ในการใช้งาน"</string> </resources> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index 61779ad7c..447baef79 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Mga setting ng Android keyboard"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Pagwawasto sa Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Mag-vibrate sa keypress"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Tunog sa keypress"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Popup sa keypress"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Ipakita sa portrait mode"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Palaging itago"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Ipakita ang key ng mga setting"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Awtomatiko"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Palaging ipakita"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Palaging itago"</string> <string name="auto_correction" msgid="4979925752001319458">"Awtomatikong pagwasto"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Awto tinatama ng spacebar at bantas ang maling na-type"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Naka-off"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Tapos na"</string> <string name="label_send_key" msgid="2815056534433717444">"Ipadala"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Higit pa"</string> <string name="label_pause_key" msgid="181098308428035340">"Pause"</string> <string name="label_wait_key" msgid="6402152600878093134">"Intay"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic sa keyboard ng mga simbolo"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hindi pinagana ang voice input"</string> <string name="selectInputMethod" msgid="315076553378705821">"Pumili ng paraan ng pag-input"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Pumili ng mga wika ng input"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Pinduting muli upang i-save"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Pindutin upang itama ang mga salita"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Pindutin ang mga inilagay na salita upang iwasto ang mga ito, kapag nakikita lang ang mga suhestiyon"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema ng keyboard"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"German na QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Ingles (UK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Ingles (Estados Unidos)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Study mode ng pagiging kapaki-pakinabang"</string> </resources> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 6c67bc0b5..91db66e3c 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android klavyesi"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye ayarları"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android düzeltme"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Tuşa basıldığında ses çıkar"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Tuşa basıldığında pop-up aç"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Dikey modda göster"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Her zaman gizle"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Ayarları göster tuşu"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Otomatik"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Her zaman göster"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Her zaman gizle"</string> <string name="auto_correction" msgid="4979925752001319458">"Otomatik düzeltme"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Boşluk çbğ ve nokt işr yanlış yazılan kelimeleri oto düzeltir"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Kapalı"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Bitti"</string> <string name="label_send_key" msgid="2815056534433717444">"Gönder"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Diğer"</string> <string name="label_pause_key" msgid="181098308428035340">"Durkl"</string> <string name="label_wait_key" msgid="6402152600878093134">"Bekle"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Simge klavysnd mikrf"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Sesle grş devre dışı"</string> <string name="selectInputMethod" msgid="315076553378705821">"Giriş yöntemini seç"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Giriş dillerini seçin"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Kaydetmek için tekrar dokunun"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Kelimeleri düzeltmek için dokunun"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Yalnızca öneriler görünür olduğunda, düzeltmek için girilen kelimelere dokunun"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Klavye teması"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Almanca QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"İngilizce (BK)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"İngilizce (ABD)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Kullanılabilirlik çalışması modu"</string> </resources> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 9d089a0cc..3e55f469e 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Клавіатура Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Налашт-ня клавіат. Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Виправлення Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібр при натиску клав."</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натиску клав."</string> <string name="popup_on_keypress" msgid="123894815723512944">"Сплив. при нат.клав."</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показувати в книжковому режимі"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Завжди ховати"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Показ. клав. налашт."</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Автоматично"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Завжди показ."</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Завжди ховати"</string> <string name="auto_correction" msgid="4979925752001319458">"Автомат. виправлення"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Пробіл і пунктуація автоматично виправляють слова з помилками"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Вимк."</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Готово"</string> <string name="label_send_key" msgid="2815056534433717444">"Надісл."</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"Алфавіт"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"Більше"</string> <string name="label_pause_key" msgid="181098308428035340">"Пауза"</string> <string name="label_wait_key" msgid="6402152600878093134">"Чек."</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Miкр. на симв. клавіат."</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голос. ввід вимкнено"</string> <string name="selectInputMethod" msgid="315076553378705821">"Вибрати метод введення"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"Налаштування методів введення"</string> <string name="language_selection_title" msgid="1651299598555326750">"Мови вводу"</string> <string name="select_language" msgid="2573265881207142437">"Вибрати мову введення"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Торкн. ще, щоб збер."</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Торкн., щоб виправ. слова"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Торкніться введених слів, щоб виправити їх, лише коли ввімкнено пропозиції"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавіатури"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Німецька клавіатура QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Англійська (Великобританія)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Англійська (США)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим вивчення зручності у використанні"</string> </resources> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 3dfcf16ad..caf9d579e 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Bàn phím Android"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Cài đặt bàn phím Android"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Dịch vụ sửa chính tả của Android"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Âm thanh khi nhấn phím"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Cửa sổ bật lên khi nhấn phím"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Hiển thị trên chế độ khổ đứng"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Luôn ẩn"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Hiển thị phím cài đặt"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Tự động"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Luôn hiển thị"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Luôn ẩn"</string> <string name="auto_correction" msgid="4979925752001319458">"Tự động sửa"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"Phím cách và dấu câu tự động sửa từ nhập sai"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Tắt"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"Xong"</string> <string name="label_send_key" msgid="2815056534433717444">"Gửi"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Khác"</string> <string name="label_pause_key" msgid="181098308428035340">"Tạm dừng"</string> <string name="label_wait_key" msgid="6402152600878093134">"Đợi"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrô trên bàn phím biểu tượng"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Nhập liệu bằng giọng nói đã bị vô hiệu hóa"</string> <string name="selectInputMethod" msgid="315076553378705821">"Chọn phương thức nhập"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <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="select_language" msgid="2573265881207142437">"Chọn ngôn ngữ nhập"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← Chạm lại để lưu"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"Chạm để sửa từ"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"Chạm các từ đã nhập để sửa, chỉ khi các đề xuất hiển thị"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Chủ đề bàn phím"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"Bàn phím QWERTY tiếng Đức"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Tiếng Anh (Anh)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"Tiếng Anh (Mỹ)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Chế độ nghiên cứu tính khả dụng"</string> </resources> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 8a94cd8a0..cdbd64dd1 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android 键盘"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android 键盘设置"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android 更正"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按键时振动"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"按键时播放音效"</string> <string name="popup_on_keypress" msgid="123894815723512944">"按键时显示弹出窗口"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"以纵向模式显示"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"始终隐藏"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"显示设置键"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"自动"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"始终显示"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"始终隐藏"</string> <string name="auto_correction" msgid="4979925752001319458">"自动更正"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"空格键或标点自动更正拼写错误的字词"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"关闭"</string> @@ -64,6 +68,8 @@ <string name="label_done_key" msgid="2441578748772529288">"完成"</string> <string name="label_send_key" msgid="2815056534433717444">"发送"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"更多"</string> <string name="label_pause_key" msgid="181098308428035340">"暂停"</string> <string name="label_wait_key" msgid="6402152600878093134">"等待"</string> @@ -131,8 +137,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"符号键盘上的麦克风"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"语音输入功能已停用"</string> <string name="selectInputMethod" msgid="315076553378705821">"选择输入法"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"配置输入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"输入语言"</string> <string name="select_language" msgid="2573265881207142437">"选择输入语言"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← 再次触摸即可保存"</string> @@ -142,11 +147,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"触摸以更正字词"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"仅在系统显示建议后,才触摸输入的字词进行更正"</string> <string name="keyboard_layout" msgid="8451164783510487501">"键盘主题"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"德语 QWERTY 键盘"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"英语(英国)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"英语(美国)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"可用性研究模式"</string> </resources> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 38d9a2f43..fcf5118b7 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -23,6 +23,13 @@ <string name="english_ime_name" msgid="7252517407088836577">"Android 鍵盤"</string> <string name="english_ime_settings" msgid="6661589557206947774">"Android 鍵盤設定"</string> <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string> + <string name="spell_checker_service_name" msgid="2003013122022285508">"Android 修正"</string> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string> <string name="popup_on_keypress" msgid="123894815723512944">"按鍵時顯示彈出式視窗"</string> @@ -45,9 +52,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"以垂直模式顯示"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"永遠隱藏"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"顯示設定金鑰"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"自動"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"一律顯示"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"永遠隱藏"</string> <string name="auto_correction" msgid="4979925752001319458">"自動修正"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"自動插入空白鍵和標點符號鍵盤,以修正拼字錯誤"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"關閉"</string> @@ -64,6 +68,9 @@ <string name="label_done_key" msgid="2441578748772529288">"完成"</string> <string name="label_send_key" msgid="2815056534433717444">"傳送"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string> + <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) --> + <skip /> <string name="label_more_key" msgid="3760239494604948502">"更多"</string> <string name="label_pause_key" msgid="181098308428035340">"暫停"</string> <string name="label_wait_key" msgid="6402152600878093134">"等候"</string> @@ -131,8 +138,7 @@ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"符號鍵盤上的麥克風"</string> <string name="voice_input_modes_summary_off" msgid="63875609591897607">"語音輸入已停用"</string> <string name="selectInputMethod" msgid="315076553378705821">"選取輸入法"</string> - <!-- no translation found for configure_input_method (373356270290742459) --> - <skip /> + <string name="configure_input_method" msgid="373356270290742459">"設定輸入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string> <string name="select_language" msgid="2573265881207142437">"選取輸入語言"</string> <string name="hint_add_to_dictionary" msgid="9006292060636342317">"← 再次輕觸即可儲存"</string> @@ -142,11 +148,8 @@ <string name="prefs_enable_recorrection" msgid="4588408906649533582">"輕觸此處可修正字詞"</string> <string name="prefs_enable_recorrection_summary" msgid="5082041365862396329">"輕觸輸入的字詞即可加以修正 (出現建議時才適用)"</string> <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</string> - <!-- no translation found for subtype_de_qwerty (3358900499589259491) --> - <skip /> - <!-- no translation found for subtype_en_GB (88170601942311355) --> - <skip /> - <!-- no translation found for subtype_en_US (6160452336634534239) --> - <skip /> + <string name="subtype_de_qwerty" msgid="3358900499589259491">"德文 QWERTY"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英式)"</string> + <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美式)"</string> <string name="prefs_usability_study_mode" msgid="1261130555134595254">"使用性研究模式"</string> </resources> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index de59bcc2d..b4688fd93 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -23,6 +23,14 @@ <string name="english_ime_name" msgid="7252517407088836577">"Ikhibhodi ye-Android"</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> + <!-- no translation found for spell_checker_service_name (2003013122022285508) --> + <skip /> + <!-- no translation found for android_spell_checker_settings (5822324635435443689) --> + <skip /> + <!-- no translation found for use_proximity_option_title (7469233942295924620) --> + <skip /> + <!-- no translation found for use_proximity_option_summary (2857708859847261945) --> + <skip /> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Dlidlizelisa ngokucindezela inkinobho"</string> <string name="sound_on_keypress" msgid="6093592297198243644">"Umsindo wokucindezela ukhiye"</string> <string name="popup_on_keypress" msgid="123894815723512944">"Ugaxekile ngokucindezela ukhiye"</string> @@ -58,9 +66,6 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Bonisa kwimodi emile"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Fihla njalo"</string> <string name="prefs_settings_key" msgid="4623341240804046498">"Bonisa ukhiye wezilungiselelo"</string> - <string name="settings_key_mode_auto_name" msgid="2993460277873684680">"Okuzenzakalelayo"</string> - <string name="settings_key_mode_always_show_name" msgid="3047567041784760575">"Bonisa njalo"</string> - <string name="settings_key_mode_always_hide_name" msgid="7833948046716923994">"Fihla njalo"</string> <string name="auto_correction" msgid="4979925752001319458">"Ukulungisa okuzenzakalelayo"</string> <!-- outdated translation 6881047311475758267 --> <string name="auto_correction_summary" msgid="5625751551134658006">"Ngokuzenzakalelayo ilungisa igama elandulele"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Valiwe"</string> @@ -80,6 +85,9 @@ <string name="label_done_key" msgid="2441578748772529288">"Kwenziwe"</string> <string name="label_send_key" msgid="2815056534433717444">"Thumela"</string> <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string> + <!-- no translation found for label_to_symbol_key (8516904117128967293) --> + <skip /> + <string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"123"</string> <string name="label_more_key" msgid="3760239494604948502">"Okungaphezulu"</string> <string name="label_pause_key" msgid="181098308428035340">"Misa okwesikhashana"</string> <string name="label_wait_key" msgid="6402152600878093134">"Linda"</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index e503d98a4..c2200b5ad 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -107,6 +107,7 @@ </declare-styleable> <declare-styleable name="CandidateView"> + <attr name="autoCorrectionVisualFlashEnabled" format="boolean" /> <attr name="autoCorrectHighlight" format="integer"> <flag name="autoCorrectBold" value="0x01" /> <flag name="autoCorrectUnderline" value="0x02" /> @@ -116,6 +117,7 @@ <attr name="colorAutoCorrect" format="color" /> <attr name="colorSuggested" format="color" /> <attr name="candidateCountInStrip" format="integer" /> + <attr name="centerCandidatePercentile" format="integer" /> </declare-styleable> <declare-styleable name="Keyboard"> @@ -154,15 +156,14 @@ <attr name="isRtlKeyboard" format="boolean" /> <!-- Icon set for key top and key preview. --> <attr name="iconShiftKey" format="reference" /> - <attr name="iconToSymbolKey" format="reference" /> - <attr name="iconToSymbolKeyWithShortcut" format="reference" /> <attr name="iconDeleteKey" format="reference" /> <attr name="iconSettingsKey" format="reference" /> - <attr name="iconShortcutKey" format="reference" /> <attr name="iconSpaceKey" format="reference" /> <attr name="iconReturnKey" format="reference" /> <attr name="iconSearchKey" format="reference" /> <attr name="iconTabKey" format="reference" /> + <attr name="iconShortcutKey" format="reference" /> + <attr name="iconShortcutForLabel" format="reference" /> <attr name="iconShiftedShiftKey" format="reference" /> <attr name="iconPreviewTabKey" format="reference" /> <attr name="iconPreviewSettingsKey" format="reference" /> @@ -202,32 +203,33 @@ <flag name="hasPopupHint" value="0x200" /> <flag name="hasUppercaseLetter" value="0x400" /> <flag name="hasHintLabel" value="0x800" /> + <flag name="withIconLeft" value="0x1000" /> + <flag name="withIconRight" value="0x2000" /> </attr> <!-- The icon to display on the key instead of the label. --> <attr name="keyIcon" format="enum"> <!-- This should be aligned with KeyboardIcons.ICON_* --> <enum name="iconShiftKey" value="1" /> - <enum name="iconToSymbolKey" value="2" /> - <enum name="iconToSymbolKeyWithShortcut" value="3" /> - <enum name="iconDeleteKey" value="4" /> - <enum name="iconSettingsKey" value="5" /> - <enum name="iconShortcutKey" value="6" /> - <enum name="iconSpaceKey" value="7" /> - <enum name="iconReturnKey" value="8" /> - <enum name="iconSearchKey" value="9" /> - <enum name="iconTabKey" value="10" /> + <enum name="iconDeleteKey" value="2" /> + <enum name="iconSettingsKey" value="3" /> + <enum name="iconSpaceKey" value="4" /> + <enum name="iconReturnKey" value="5" /> + <enum name="iconSearchKey" value="6" /> + <enum name="iconTabKey" value="7" /> + <enum name="iconShortcutKey" value="8" /> + <enum name="iconShortcutForLabel" value="9" /> </attr> <!-- Shift key icon for shifted state --> <attr name="keyIconShifted" format="enum"> <!-- This should be aligned with KeyboardIcons.ICON_SHIFTED_* --> - <enum name="iconShiftedShiftKey" value="11" /> + <enum name="iconShiftedShiftKey" value="10" /> </attr> <!-- The icon to show in the popup preview. --> <attr name="keyIconPreview" format="enum"> <!-- This should be aligned with KeyboardIcons.ICON_PREVIEW_* --> - <enum name="iconPreviewTabKey" value="12" /> - <enum name="iconPreviewSettingsKey" value="13" /> - <enum name="iconPreviewShortcutKey" value="14" /> + <enum name="iconPreviewTabKey" value="11" /> + <enum name="iconPreviewSettingsKey" value="12" /> + <enum name="iconPreviewShortcutKey" value="13" /> </attr> <!-- The key style to specify a set of key attributes defined by <key_style/> --> <attr name="keyStyle" format="string" /> @@ -266,8 +268,8 @@ <enum name="shortcutImeOrSettings" value="3" /> </attr> <attr name="clobberSettingsKey" format="boolean" /> - <attr name="voiceKeyEnabled" format="boolean" /> - <attr name="hasVoiceKey" format="boolean" /> + <attr name="shortcutKeyEnabled" format="boolean" /> + <attr name="hasShortcutKey" format="boolean" /> <attr name="imeAction" format="enum"> <!-- This should be aligned with EditorInfo.IME_ACTION_* --> <enum name="actionUnspecified" value="0" /> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index ed7e0e5ab..6327ede38 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -20,9 +20,10 @@ <resources> <bool name="config_enable_show_settings_key_option">true</bool> + <bool name="config_default_show_settings_key">false</bool> <bool name="config_enable_show_voice_key_option">true</bool> <bool name="config_enable_show_popup_on_keypress_option">true</bool> - <bool name="config_enable_show_recorrection_option">true</bool> + <bool name="config_enable_show_recorrection_option">false</bool> <bool name="config_enable_bigram_suggestions_option">true</bool> <bool name="config_enable_usability_study_mode_option">false</bool> <bool name="config_sliding_key_input_enabled">true</bool> @@ -35,9 +36,10 @@ <!-- Default value for bigram prediction: after entering a word and a space only, should we look at input history to suggest a hopefully helpful candidate for the next word? --> <bool name="config_default_bigram_prediction">false</bool> - <bool name="config_default_recorrection_enabled">true</bool> + <bool name="config_default_compat_recorrection_enabled">true</bool> <bool name="config_default_sound_enabled">false</bool> <bool name="config_auto_correction_spacebar_led_enabled">true</bool> + <bool name="config_auto_correction_suggestion_strip_visual_flash_enabled">false</bool> <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false --> <bool name="config_show_mini_keyboard_at_touched_point">false</bool> <!-- The language is never displayed if == 0, always displayed if < 0 --> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index ff0458cfe..2c4b35ef8 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -57,7 +57,7 @@ <dimen name="mini_keyboard_vertical_correction">-0.330in</dimen> <!-- We use "inch", not "dip" because this value tries dealing with physical distance related to user's finger. --> - <dimen name="keyboard_vertical_correction">-0.05in</dimen> + <dimen name="keyboard_vertical_correction">0.0in</dimen> <fraction name="key_letter_ratio">55%</fraction> <fraction name="key_large_letter_ratio">65%</fraction> @@ -86,6 +86,7 @@ <dimen name="candidate_padding">6dip</dimen> <dimen name="candidate_text_size">18dip</dimen> <integer name="candidate_count_in_strip">3</integer> + <integer name="center_candidate_percentile">36</integer> <!-- If the screen height in landscape is larger than the below value, then the keyboard will not go into extract (fullscreen) mode. --> diff --git a/java/res/values/donottranslate-altchars.xml b/java/res/values/donottranslate-altchars.xml index 38d406c91..fbde4b9ba 100644 --- a/java/res/values/donottranslate-altchars.xml +++ b/java/res/values/donottranslate-altchars.xml @@ -50,9 +50,7 @@ <string name="alternates_for_currency_general">¢,$,€,£,¥,₱</string> <string name="alternates_for_smiley">":-)|:-) ,:-(|:-( ,;-)|;-) ,:-P|:-P ,=-O|=-O ,:-*|:-* ,:O|:O ,B-)|B-) ,:-$|:-$ ,:-!|:-! ,:-[|:-[ ,O:-)|O:-) ,:-\\\\\\\\|:-\\\\\\\\ ,:\'(|:\'( ,:-D|:-D "</string> <string name="alternates_for_punctuation">"\\,,\?,!,:,-,\',\",(,),/,;,+,&,\@"</string> - <string name="alternates_for_web_tab_punctuation">".,\\,,\?,!,:,-,\',\",(,),/,;,+,&,\@"</string> <integer name="popup_keyboard_column_for_punctuation">7</integer> - <integer name="popup_keyboard_column_for_web_punctuation">8</integer> <string name="keyhintlabel_for_punctuation"></string> <string name="keylabel_for_popular_domain">".com"</string> <!-- popular web domains for the locale - most popular, displayed on the keyboard --> @@ -77,14 +75,17 @@ <string name="alternates_for_symbols_8"></string> <string name="alternates_for_symbols_9"></string> <string name="alternates_for_symbols_0">ⁿ,∅</string> - <string name="keylabel_for_symbols_comma">,</string> - <string name="keylabel_for_symbols_f1">,</string> + <string name="keylabel_for_comma">,</string> + <string name="keylabel_for_f1">,</string> <string name="keylabel_for_symbols_question">\?</string> <string name="keylabel_for_symbols_semicolon">;</string> <string name="keylabel_for_symbols_percent">%</string> - <string name="alternates_for_symbols_comma"></string> - <!-- @icon/5 is iconsSettingsKey --> - <string name="alternates_for_symbols_f1">\@icon/5|\@integer/key_settings</string> + <string name="alternates_for_comma"></string> + <string name="alternates_for_f1"></string> + <!-- @icon/3 is iconSettingsKey --> + <string name="alternates_for_f1_settings">\@icon/3|\@integer/key_settings</string> + <!-- @icon/7 is iconTabKey --> + <string name="alternates_for_f1_navigate">\@icon/7|\@integer/key_tab</string> <string name="alternates_for_symbols_question">¿</string> <string name="alternates_for_symbols_semicolon"></string> <string name="alternates_for_symbols_percent">‰</string> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 4cf930399..f55e9bf53 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -19,7 +19,7 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Symbols that are suggested between words --> - <string name="suggested_punctuations">!?,\u0022\u0027:();-/@_</string> + <string name="suggested_punctuations">!?,\u0022:;()\u0027-/@_</string> <!-- Symbols that should be swapped with a magic space --> <string name="magic_space_swapping_symbols">.,;:!?)]}\u0022</string> <!-- Symbols that should strip a magic space --> @@ -27,21 +27,22 @@ <!-- Symbols that should convert magic spaces into real space --> <string name="magic_space_promoting_symbols">([*&@{<>+=|</string> <!-- Symbols that do NOT separate words --> - <string name="non_word_separator_symbols">\u0027-</string> + <string name="symbols_excluded_from_word_separators">\u0027-</string> <!-- Word separator list is the union of all symbols except those that are not separators: magic_space_swapping_symbols | magic_space_stripping_symbols | - magic_space_neutral_symbols \ non_word_separator_symbols --> + magic_space_neutral_symbols \ symbols_excluded_from_word_separators --> + + <!-- Label for "switch to more symbol" modifier key. Must be short to fit on key! --> + <string name="label_to_more_symbol_key">= \\ <</string> + <!-- Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! --> + <string name="label_to_more_symbol_for_tablet_key">\\ ^ [ {</string> - <!-- Label for ALT modifier key. Must be short to fit on key! --> - <string name="label_alt_key">ALT</string> <!-- Label for "Tab" key. Must be short to fit on key! --> <string name="label_tab_key">Tab</string> - <!-- Label for "switch to symbols" key. Must be short to fit on key! --> - <string name="label_to_symbol_key">\?123</string> <!-- Label for "switch to phone numeric" key. Must be short to fit on key! --> <string name="label_to_phone_numeric_key">123</string> <!-- Label for "switch to phone symbols" key. Must be short to fit on key! --> - <string name="label_to_phone_symbols_key">\uff0a\uff03\uff08</string> + <string name="label_to_phone_symbols_key">\uff0a\uff03</string> <!-- Character for candidate divider (BOX DRAWINGS LIGHT VERTICAL) --> <string name="label_candidate_divider">\u2502</string> @@ -50,26 +51,6 @@ <!-- Character for close candidates pane (BLACK UP-POINTING TRIANGLE) --> <string name="label_close_candidates_pane">\u25b2</string> - <!-- Option values to show/hide the settings key in onscreen keyboard --> - <!-- Automatically decide to show or hide the settings key --> - <string name="settings_key_mode_auto">0</string> - <!-- Always show the settings key --> - <string name="settings_key_mode_always_show">1</string> - <!-- Always hide the settings key --> - <string name="settings_key_mode_always_hide">2</string> - <!-- Array of the settings key mode values --> - <string-array name="settings_key_modes_values"> - <item>@string/settings_key_mode_auto</item> - <item>@string/settings_key_mode_always_show</item> - <item>@string/settings_key_mode_always_hide</item> - </string-array> - <!-- Array of the settings key modes --> - <string-array name="settings_key_modes"> - <item>@string/settings_key_mode_auto_name</item> - <item>@string/settings_key_mode_always_show_name</item> - <item>@string/settings_key_mode_always_hide_name</item> - </string-array> - <!-- Always show the suggestion strip --> <string name="prefs_suggestion_visibility_show_value">0</string> <!-- Show the suggestion strip only on portrait mode --> @@ -172,4 +153,8 @@ <!-- Generic subtype label --> <string name="subtype_generic">%s</string> + + <!-- dictionary pack package name /settings activity (for shared prefs and settings) --> + <string name="dictionary_pack_package_name">com.google.android.inputmethod.latin.dictionarypack</string> + <string name="dictionary_pack_settings_activity">com.google.android.inputmethod.latin.dictionarypack.DictionarySettingsActivity</string> </resources> diff --git a/java/res/values/keyboard-icons-black.xml b/java/res/values/keyboard-icons-black.xml index ff758dc4b..23d163c69 100644 --- a/java/res/values/keyboard-icons-black.xml +++ b/java/res/values/keyboard-icons-black.xml @@ -22,14 +22,14 @@ sym_bkeyboard_voice_off --> <item name="iconShiftKey">@drawable/sym_bkeyboard_shift</item> - <item name="iconToSymbolKeyWithShortcut">@drawable/sym_bkeyboard_123_mic</item> <item name="iconDeleteKey">@drawable/sym_bkeyboard_delete</item> <item name="iconSettingsKey">@drawable/sym_bkeyboard_settings</item> - <item name="iconShortcutKey">@drawable/sym_bkeyboard_mic</item> <item name="iconSpaceKey">@drawable/sym_bkeyboard_space</item> <item name="iconReturnKey">@drawable/sym_bkeyboard_return</item> <item name="iconSearchKey">@drawable/sym_bkeyboard_search</item> <item name="iconTabKey">@drawable/sym_bkeyboard_tab</item> + <item name="iconShortcutKey">@drawable/sym_bkeyboard_mic</item> + <item name="iconShortcutForLabel">@drawable/sym_bkeyboard_label_mic</item> <item name="iconShiftedShiftKey">@drawable/sym_bkeyboard_shift_locked</item> <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item> <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_feedback_settings</item> diff --git a/java/res/values/keyboard-icons-ics.xml b/java/res/values/keyboard-icons-ics.xml index 62859e3a3..f619691a9 100644 --- a/java/res/values/keyboard-icons-ics.xml +++ b/java/res/values/keyboard-icons-ics.xml @@ -21,14 +21,14 @@ sym_keyboard_123_mic_holo --> <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo</item> - <item name="iconToSymbolKeyWithShortcut">@drawable/sym_keyboard_123_mic_holo</item> <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo</item> <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo</item> - <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item> <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo</item> <item name="iconReturnKey">@drawable/sym_keyboard_return_holo</item> <item name="iconSearchKey">@drawable/sym_keyboard_search</item> <item name="iconTabKey">@drawable/sym_keyboard_tab_holo</item> + <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item> + <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo</item> <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked_holo</item> <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item> <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_settings_holo</item> diff --git a/java/res/values/keyboard-icons-white.xml b/java/res/values/keyboard-icons-white.xml index 3c28cb771..a1765a44d 100644 --- a/java/res/values/keyboard-icons-white.xml +++ b/java/res/values/keyboard-icons-white.xml @@ -18,14 +18,14 @@ <style name="KeyboardIcons"> <!-- Keyboard icons --> <item name="iconShiftKey">@drawable/sym_keyboard_shift</item> - <item name="iconToSymbolKeyWithShortcut">@drawable/sym_keyboard_123_mic</item> <item name="iconDeleteKey">@drawable/sym_keyboard_delete</item> <item name="iconSettingsKey">@drawable/sym_keyboard_settings</item> - <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item> <item name="iconSpaceKey">@drawable/sym_keyboard_space</item> <item name="iconReturnKey">@drawable/sym_keyboard_return</item> <item name="iconSearchKey">@drawable/sym_keyboard_search</item> <item name="iconTabKey">@drawable/sym_keyboard_tab</item> + <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item> + <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic</item> <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked</item> <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item> <item name="iconPreviewSettingsKey">@drawable/sym_keyboard_feedback_settings</item> diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml index ee345291a..59cc07531 100644 --- a/java/res/values/keycodes.xml +++ b/java/res/values/keycodes.xml @@ -27,5 +27,5 @@ <integer name="key_switch_alpha_symbol">-2</integer> <integer name="key_delete">-5</integer> <integer name="key_settings">-6</integer> - <integer name="key_shortcut">-8</integer> + <integer name="key_shortcut">-7</integer> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index e5b85fec3..247bdba41 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -28,6 +28,15 @@ <!-- Title for Latin Keyboard spell checker service --> <string name="spell_checker_service_name">Android correction</string> + <!-- Title for the spell checking service settings screen --> + <string name="android_spell_checker_settings">Spell checking settings</string> + + <!-- Title for the "use proximity" option for spell checking [CHAR LIMIT=25] --> + <string name="use_proximity_option_title">Use proximity data</string> + + <!-- Description for the "use proximity" option for spell checking [CHAR LIMIT=65] --> + <string name="use_proximity_option_summary">Use a keyboard-like proximity algorithm for spell checking</string> + <!-- Option to provide vibrate/haptic feedback on keypress --> <string name="vibrate_on_keypress">Vibrate on keypress</string> @@ -82,12 +91,6 @@ <!-- Option to show/hide the settings key --> <string name="prefs_settings_key">Show settings key</string> - <!-- Option to automatically decide to show/hide the settings key --> - <string name="settings_key_mode_auto_name">Automatic</string> - <!-- Option to always show the settings key --> - <string name="settings_key_mode_always_show_name">Always show</string> - <!-- Option to always hide the settings key --> - <string name="settings_key_mode_always_hide_name">Always hide</string> <!-- Option to decide the auto correction threshold score --> <!-- Option to enable auto correction [CHAR LIMIT=20]--> @@ -115,17 +118,22 @@ <!-- Indicates that a word has been added to the dictionary --> <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string> - <!-- Label for soft enter key when it performs GO action. Must be short to fit on key! --> + <!-- Label for soft enter key when it performs GO action. Must be short to fit on key! [CHAR LIMIT=5] --> <string name="label_go_key">Go</string> - <!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! --> + <!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! [CHAR LIMIT=5] --> <string name="label_next_key">Next</string> - <!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! --> + <!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! [CHAR LIMIT=5] --> <string name="label_done_key">Done</string> - <!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! --> + <!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! [CHAR LIMIT=5] --> <string name="label_send_key">Send</string> - <!-- Label for "switch to alphabetic" key. Must be short to fit on key! --> + <!-- Label for "switch to alphabetic" key. Must be short to fit on key! [CHAR LIMIT=3] --> <string name="label_to_alpha_key">ABC</string> - <!-- Label for Shift modifier key of symbol keyboard. Must be short to fit on key! --> + <!-- Label for "switch to symbols" key. Must be short to fit on key! [CHAR LIMIT=4] --> + <string name="label_to_symbol_key">\?123</string> + <!-- Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" + part because it'll be appended by the code. Must be short to fit on key! [CHAR LIMIT=3] --> + <string name="label_to_symbol_with_microphone_key">123</string> + <!-- Label for Shift modifier key of symbol keyboard. Must be short to fit on key! [CHAR LIMIT=5] --> <string name="label_more_key">More</string> <!-- Label for "Pause" key of phone number keyboard. Must be short to fit on key! [CHAR LIMIT=5] --> <string name="label_pause_key">Pause</string> diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index 9f91ef57c..a47eeed12 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -85,11 +85,13 @@ <item name="android:background">@drawable/candidate_feedback_background</item> </style> <style name="CandidateViewStyle" parent="SuggestionsStripBackgroundStyle"> + <item name="autoCorrectionVisualFlashEnabled">@bool/config_auto_correction_suggestion_strip_visual_flash_enabled</item> <item name="autoCorrectHighlight">autoCorrectBold</item> <item name="colorTypedWord">#FFFFFFFF</item> <item name="colorAutoCorrect">#FFFCAE00</item> <item name="colorSuggested">#FFFCAE00</item> <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item> + <item name="centerCandidatePercentile">@integer/center_candidate_percentile</item> </style> <!-- Theme "Basic high contrast" --> <style name="KeyboardView.HighContrast" parent="KeyboardView"> @@ -187,11 +189,13 @@ <item name="android:background">@drawable/keyboard_popup_panel_background_holo</item> </style> <style name="CandidateViewStyle.IceCreamSandwich" parent="SuggestionsStripBackgroundStyle.IceCreamSandwich"> + <item name="autoCorrectionVisualFlashEnabled">@bool/config_auto_correction_suggestion_strip_visual_flash_enabled</item> <item name="autoCorrectHighlight">autoCorrectBold|autoCorrectInvert</item> <item name="colorTypedWord">#FFFFFFFF</item> <item name="colorAutoCorrect">#FF3DC8FF</item> <item name="colorSuggested">#FFFFFFFF</item> <item name="candidateCountInStrip">@integer/candidate_count_in_strip</item> + <item name="centerCandidatePercentile">@integer/center_candidate_percentile</item> </style> <style name="PopupMiniKeyboardAnimation"> <item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item> diff --git a/java/res/xml-sw600dp/kbd_key_styles.xml b/java/res/xml-sw600dp/kbd_key_styles.xml index f94309911..05b683707 100644 --- a/java/res/xml-sw600dp/kbd_key_styles.xml +++ b/java/res/xml-sw600dp/kbd_key_styles.xml @@ -31,14 +31,14 @@ latin:clobberSettingsKey="true" > <key-style - latin:styleName="settingsPopupStyle" + latin:styleName="f2PopupStyle" latin:parentStyle="functionalKeyStyle" /> </case> <default> <key-style - latin:styleName="settingsPopupStyle" + latin:styleName="f2PopupStyle" latin:keyLabelOption="hasPopupHint" - latin:popupCharacters="\@icon/5|\@integer/key_settings" + latin:popupCharacters="\@icon/3|\@integer/key_settings" latin:parentStyle="functionalKeyStyle" /> </default> </switch> @@ -79,7 +79,7 @@ latin:code="@integer/key_shortcut" latin:keyIcon="iconShortcutKey" latin:keyIconPreview="iconPreviewShortcutKey" - latin:parentStyle="settingsPopupStyle" /> + latin:parentStyle="f2PopupStyle" /> <key-style latin:styleName="settingsKeyStyle" latin:code="@integer/key_settings" @@ -105,12 +105,17 @@ latin:keyLabelOption="fontNormal" latin:parentStyle="functionalKeyStyle" /> <key-style - latin:styleName="moreKeyStyle" + latin:styleName="toMoreSymbolKeyStyle" latin:code="@integer/key_shift" - latin:keyLabel="@string/label_more_key" + latin:keyLabel="@string/label_to_more_symbol_for_tablet_key" latin:keyLabelOption="fontNormal" - latin:parentStyle="functionalKeyStyle" - latin:isSticky="true" /> + latin:parentStyle="functionalKeyStyle" /> + <key-style + latin:styleName="backFromMoreSymbolKeyStyle" + latin:code="@integer/key_shift" + latin:keyLabel="@string/label_to_symbol_key" + latin:keyLabelOption="fontNormal" + latin:parentStyle="functionalKeyStyle" /> <key-style latin:styleName="comKeyStyle" latin:keyLabel="@string/keylabel_for_popular_domain" diff --git a/java/res/xml-sw600dp/kbd_phone.xml b/java/res/xml-sw600dp/kbd_phone.xml index 7b584c142..0331389c1 100644 --- a/java/res/xml-sw600dp/kbd_phone.xml +++ b/java/res/xml-sw600dp/kbd_phone.xml @@ -76,7 +76,7 @@ </Row> <Row> <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="toMoreSymbolKeyStyle" latin:keyWidth="11.0%p" /> <Key latin:keyLabel="(" diff --git a/java/res/xml-sw600dp/kbd_phone_shift.xml b/java/res/xml-sw600dp/kbd_phone_shift.xml index 483964526..2b6936dce 100644 --- a/java/res/xml-sw600dp/kbd_phone_shift.xml +++ b/java/res/xml-sw600dp/kbd_phone_shift.xml @@ -84,7 +84,7 @@ </Row> <Row> <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="backFromMoreSymbolKeyStyle" latin:keyWidth="11.00%p" /> <Key latin:keyLabel="(" diff --git a/java/res/xml-sw600dp/kbd_qwerty_f2.xml b/java/res/xml-sw600dp/kbd_qwerty_f2.xml index 3ea11805f..b25afc12f 100644 --- a/java/res/xml-sw600dp/kbd_qwerty_f2.xml +++ b/java/res/xml-sw600dp/kbd_qwerty_f2.xml @@ -34,13 +34,13 @@ > <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" latin:keyWidth="fillBoth" /> </case> - <!-- voiceKeyEnabled="false" --> + <!-- shortcutKeyEnabled="false" --> <default> <Spacer /> </default> @@ -51,13 +51,13 @@ > <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" latin:keyWidth="fillBoth" /> </case> - <!-- voiceKeyEnabled="false" --> + <!-- shortcutKeyEnabled="false" --> <default> <Key latin:keyStyle="settingsKeyStyle" diff --git a/java/res/xml-sw600dp/kbd_rows_russian.xml b/java/res/xml-sw600dp/kbd_rows_russian.xml index 7588f6ccb..2f4b95e60 100644 --- a/java/res/xml-sw600dp/kbd_rows_russian.xml +++ b/java/res/xml-sw600dp/kbd_rows_russian.xml @@ -105,8 +105,7 @@ <Key latin:keyLabel="т" /> <Key - latin:keyLabel="ь" - latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" /> + latin:keyLabel="ь" /> <Key latin:keyLabel="б" /> <Key diff --git a/java/res/xml-sw600dp/kbd_rows_symbols.xml b/java/res/xml-sw600dp/kbd_rows_symbols.xml index 9982f4575..4f6a9bc31 100644 --- a/java/res/xml-sw600dp/kbd_rows_symbols.xml +++ b/java/res/xml-sw600dp/kbd_rows_symbols.xml @@ -100,7 +100,7 @@ latin:keyWidth="8.9%p" > <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="toMoreSymbolKeyStyle" latin:keyWidth="10.0%p" /> <Key latin:keyLabel="<" @@ -128,8 +128,8 @@ latin:keyLabel="@string/keylabel_for_symbols_semicolon" latin:popupCharacters="@string/alternates_for_symbols_semicolon" /> <Key - latin:keyLabel="@string/keylabel_for_symbols_comma" - latin:popupCharacters="@string/alternates_for_symbols_comma" /> + latin:keyLabel="@string/keylabel_for_comma" + latin:popupCharacters="@string/alternates_for_comma" /> <Key latin:keyLabel="." /> <Key diff --git a/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml b/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml index ac4b93fa4..1dca8c47e 100644 --- a/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml +++ b/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml @@ -101,7 +101,7 @@ latin:keyWidth="8.9%p" > <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="backFromMoreSymbolKeyStyle" latin:keyWidth="10.0%p" /> <Key latin:keyLabel="\\" /> diff --git a/java/res/xml-sw768dp/kbd_key_styles.xml b/java/res/xml-sw768dp/kbd_key_styles.xml index 6e4818dc2..1711c423f 100644 --- a/java/res/xml-sw768dp/kbd_key_styles.xml +++ b/java/res/xml-sw768dp/kbd_key_styles.xml @@ -87,12 +87,17 @@ latin:keyLabelOption="fontNormal" latin:parentStyle="functionalKeyStyle" /> <key-style - latin:styleName="moreKeyStyle" + latin:styleName="toMoreSymbolKeyStyle" latin:code="@integer/key_shift" - latin:keyLabel="@string/label_more_key" + latin:keyLabel="@string/label_to_more_symbol_for_tablet_key" latin:keyLabelOption="fontNormal" - latin:parentStyle="functionalKeyStyle" - latin:isSticky="true" /> + latin:parentStyle="functionalKeyStyle" /> + <key-style + latin:styleName="backFromMoreSymbolKeyStyle" + latin:code="@integer/key_shift" + latin:keyLabel="@string/label_to_symbol_key" + latin:keyLabelOption="fontNormal" + latin:parentStyle="functionalKeyStyle" /> <key-style latin:styleName="comKeyStyle" latin:keyLabel="@string/keylabel_for_popular_domain" diff --git a/java/res/xml-sw768dp/kbd_number.xml b/java/res/xml-sw768dp/kbd_number.xml index 1706eaac2..369e91a77 100644 --- a/java/res/xml-sw768dp/kbd_number.xml +++ b/java/res/xml-sw768dp/kbd_number.xml @@ -209,7 +209,7 @@ latin:keyStyle="numKeyStyle" /> <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml-sw768dp/kbd_phone.xml b/java/res/xml-sw768dp/kbd_phone.xml index 945b641ea..e55b1841a 100644 --- a/java/res/xml-sw768dp/kbd_phone.xml +++ b/java/res/xml-sw768dp/kbd_phone.xml @@ -54,7 +54,7 @@ </Row> <Row> <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="toMoreSymbolKeyStyle" latin:keyWidth="11.172%p" /> <Key latin:keyLabel="," @@ -127,7 +127,7 @@ latin:keyStyle="numKeyStyle" /> <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml-sw768dp/kbd_phone_shift.xml b/java/res/xml-sw768dp/kbd_phone_shift.xml index 693400e67..646f0b867 100644 --- a/java/res/xml-sw768dp/kbd_phone_shift.xml +++ b/java/res/xml-sw768dp/kbd_phone_shift.xml @@ -58,7 +58,7 @@ </Row> <Row> <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="backFromMoreSymbolKeyStyle" latin:keyWidth="11.172%p" /> <Key latin:keyLabel="," @@ -139,7 +139,7 @@ latin:keyStyle="numKeyStyle" /> <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml-sw768dp/kbd_qwerty_row4.xml b/java/res/xml-sw768dp/kbd_qwerty_row4.xml index d463de9a6..1ff9d81f1 100644 --- a/java/res/xml-sw768dp/kbd_qwerty_row4.xml +++ b/java/res/xml-sw768dp/kbd_qwerty_row4.xml @@ -101,7 +101,7 @@ </switch> <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml-sw768dp/kbd_rows_symbols.xml b/java/res/xml-sw768dp/kbd_rows_symbols.xml index 1d5bd9d15..66b9789ac 100644 --- a/java/res/xml-sw768dp/kbd_rows_symbols.xml +++ b/java/res/xml-sw768dp/kbd_rows_symbols.xml @@ -107,7 +107,7 @@ latin:keyWidth="8.047%p" > <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="toMoreSymbolKeyStyle" latin:keyWidth="13.829%p" /> <Key latin:keyLabel="<" @@ -135,8 +135,8 @@ latin:keyLabel="@string/keylabel_for_symbols_semicolon" latin:popupCharacters="@string/alternates_for_symbols_semicolon" /> <Key - latin:keyLabel="@string/keylabel_for_symbols_comma" - latin:popupCharacters="@string/alternates_for_symbols_comma" /> + latin:keyLabel="@string/keylabel_for_comma" + latin:popupCharacters="@string/alternates_for_comma" /> <Key latin:keyLabel="." /> <Key @@ -146,7 +146,7 @@ latin:keyLabel="@string/keylabel_for_symbols_question" latin:popupCharacters="@string/alternates_for_symbols_question" /> <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="toMoreSymbolKeyStyle" latin:keyXPos="-13.750%p" latin:keyWidth="fillBoth" /> </Row> @@ -180,7 +180,7 @@ latin:keyLabel="_" /> <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml b/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml index 2219bdc92..1f43a0f91 100644 --- a/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml +++ b/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml @@ -108,7 +108,7 @@ latin:keyWidth="8.047%p" > <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="backFromMoreSymbolKeyStyle" latin:keyWidth="13.829%p" /> <Key latin:keyLabel="\\" /> @@ -135,7 +135,7 @@ latin:keyStyle="nonPasswordSymbolKeyStyle" latin:keyLabel="¿" /> <Key - latin:keyStyle="moreKeyStyle" + latin:keyStyle="backFromMoreSymbolKeyStyle" latin:keyXPos="-13.750%p" latin:keyWidth="fillBoth" /> </Row> @@ -155,7 +155,7 @@ latin:keyWidth="37.500%p" /> <switch> <case - latin:voiceKeyEnabled="true" + latin:shortcutKeyEnabled="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml index 6436b27f0..5612251a5 100644 --- a/java/res/xml/kbd_key_styles.xml +++ b/java/res/xml/kbd_key_styles.xml @@ -25,40 +25,43 @@ <key-style latin:styleName="functionalKeyStyle" latin:isFunctional="true" /> - <!-- Base key style for the key which may have settings key as popup key --> + <!-- 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="settingsPopupStyle" - latin:parentStyle="functionalKeyStyle" /> - <key-style - latin:styleName="symbolsF1PopupStyle" + latin:styleName="f1PopupStyle" + latin:keyLabelOption="hasPopupHint" + latin:popupCharacters="@string/alternates_for_f1" latin:parentStyle="functionalKeyStyle" /> </case> + <!-- clobberSettingsKey="false --> <case - latin:clobberSettingsKey="false" - latin:hasSettingsKey="true" + latin:hasSettingsKey="false" > <key-style - latin:styleName="settingsPopupStyle" - latin:parentStyle="functionalKeyStyle" /> - <key-style - latin:styleName="symbolsF1PopupStyle" + latin:styleName="f1PopupStyle" + latin:keyLabelOption="hasPopupHint" + latin:popupCharacters="@string/alternates_for_f1_settings" latin:parentStyle="functionalKeyStyle" /> </case> - <!-- clobberSettingsKey="false" and hasSettingsKey="false" --> - <default> + <!-- clobberSettingsKey="false" hasSettingsKey="true" --> + <case + latin:navigateAction="true" + > <key-style - latin:styleName="settingsPopupStyle" + latin:styleName="f1PopupStyle" latin:keyLabelOption="hasPopupHint" - latin:popupCharacters="\@icon/5|\@integer/key_settings" + latin:popupCharacters="@string/alternates_for_f1_navigate" latin:parentStyle="functionalKeyStyle" /> + </case> + <!-- clobberSettingsKey="false" and hasSettingsKey="true" navigateAction="false" --> + <default> <key-style - latin:styleName="symbolsF1PopupStyle" + latin:styleName="f1PopupStyle" latin:keyLabelOption="hasPopupHint" - latin:popupCharacters="@string/alternates_for_symbols_f1" + latin:popupCharacters="@string/alternates_for_f1" latin:parentStyle="functionalKeyStyle" /> </default> </switch> @@ -79,6 +82,19 @@ <!-- Return key style --> <switch> <case + latin:mode="im" + > + <!-- Smiley key. --> + <key-style + latin:styleName="returnKeyStyle" + latin:keyLabel=":-)" + latin:keyOutputText=":-) " + latin:keyLabelOption="hasPopupHint" + latin:popupCharacters="@string/alternates_for_smiley" + latin:maxPopupKeyboardColumn="5" + latin:parentStyle="functionalKeyStyle" /> + </case> + <case latin:imeAction="actionGo" > <key-style @@ -136,19 +152,11 @@ latin:code="@integer/key_space" latin:parentStyle="functionalKeyStyle" /> <key-style - latin:styleName="smileyKeyStyle" - latin:keyLabel=":-)" - latin:keyOutputText=":-) " - latin:keyLabelOption="hasPopupHint" - latin:popupCharacters="@string/alternates_for_smiley" - latin:maxPopupKeyboardColumn="5" - latin:parentStyle="functionalKeyStyle" /> - <key-style latin:styleName="shortcutKeyStyle" latin:code="@integer/key_shortcut" latin:keyIcon="iconShortcutKey" latin:keyIconPreview="iconPreviewShortcutKey" - latin:parentStyle="settingsPopupStyle" /> + latin:parentStyle="f1PopupStyle" /> <key-style latin:styleName="settingsKeyStyle" latin:code="@integer/key_settings" @@ -169,16 +177,19 @@ latin:keyIcon="iconTabKey" latin:keyIconPreview="iconPreviewTabKey" /> <switch> - <!-- When this qwerty keyboard has no voice key but voice key is enabled, then symbol - keyboard will have mic key. That means we should use "?123mic" key here. --> + <!-- When this qwerty keyboard has no shortcut keys but shortcut key is enabled, then symbol + keyboard will have a shortcut key. That means we should use + iconToSymbolKeyWithShortcutKey here. --> <case - latin:voiceKeyEnabled="true" - latin:hasVoiceKey="false" + latin:shortcutKeyEnabled="true" + latin:hasShortcutKey="false" > <key-style latin:styleName="toSymbolKeyStyle" latin:code="@integer/key_switch_alpha_symbol" - latin:keyIcon="iconToSymbolKeyWithShortcut" + latin:keyIcon="iconShortcutForLabel" + latin:keyLabel="@string/label_to_symbol_with_microphone_key" + latin:keyLabelOption="withIconRight" latin:parentStyle="functionalKeyStyle" /> </case> <default> @@ -195,11 +206,15 @@ latin:keyLabel="@string/label_to_alpha_key" latin:parentStyle="functionalKeyStyle" /> <key-style - latin:styleName="altKeyStyle" + latin:styleName="toMoreSymbolKeyStyle" latin:code="@integer/key_shift" - latin:keyLabel="@string/label_alt_key" - latin:parentStyle="functionalKeyStyle" - latin:isSticky="true" /> + latin:keyLabel="@string/label_to_more_symbol_key" + latin:parentStyle="functionalKeyStyle" /> + <key-style + latin:styleName="backFromMoreSymbolKeyStyle" + latin:code="@integer/key_shift" + latin:keyLabel="@string/label_to_symbol_key" + latin:parentStyle="functionalKeyStyle" /> <switch> <case latin:passwordInput="true" @@ -224,29 +239,11 @@ </default> </switch> <key-style - latin:styleName="basePunctuationKeyStyle" + latin:styleName="punctuationKeyStyle" latin:keyLabel="." latin:keyHintLabel="@string/keyhintlabel_for_punctuation" latin:keyLabelOption="hasPopupHint" latin:popupCharacters="@string/alternates_for_punctuation" latin:maxPopupKeyboardColumn="@integer/popup_keyboard_column_for_punctuation" latin:parentStyle="functionalKeyStyle" /> - <switch> - <case - latin:navigateAction="true" - > - <key-style - latin:styleName="punctuationKeyStyle" - latin:keyHintLabel="@string/keyhintlabel_for_punctuation" - latin:keyLabelOption="hasPopupHint" - latin:popupCharacters="@string/alternates_for_web_tab_punctuation" - latin:maxPopupKeyboardColumn="@integer/popup_keyboard_column_for_web_punctuation" - latin:parentStyle="tabKeyStyle" /> - </case> - <default> - <key-style - latin:styleName="punctuationKeyStyle" - latin:parentStyle="basePunctuationKeyStyle" /> - </default> - </switch> </merge> diff --git a/java/res/xml/kbd_qwerty_f1.xml b/java/res/xml/kbd_qwerty_f1.xml index 1546f49c9..83b6ecc8d 100644 --- a/java/res/xml/kbd_qwerty_f1.xml +++ b/java/res/xml/kbd_qwerty_f1.xml @@ -23,7 +23,8 @@ > <switch> <case - latin:hasSettingsKey="true" + latin:hasSettingsKey="false" + latin:navigateAction="false" > <switch> <case @@ -31,46 +32,30 @@ > <Key latin:keyLabel="/" - latin:keyWidth="9.2%p" - latin:keyStyle="settingsPopupStyle" /> + latin:keyStyle="f1PopupStyle" /> </case> <case latin:mode="email" > <Key latin:keyLabel="\@" - latin:keyWidth="9.2%p" - latin:keyStyle="settingsPopupStyle" /> + latin:keyStyle="f1PopupStyle" /> + </case> + <case + latin:hasShortcutKey="true" + > + <Key + latin:keyStyle="shortcutKeyStyle" /> </case> + <!-- latin:hasShortcutKey="false" --> <default> - <switch> - <case - latin:hasVoiceKey="true" - > - <Key - latin:keyStyle="shortcutKeyStyle" - latin:keyWidth="9.2%p" /> - </case> - <!-- latin:hasVoiceKey="false" --> - <case - latin:navigateAction="true" - > - <Key - latin:keyLabel="." - latin:keyWidth="9.2%p" - latin:keyStyle="settingsPopupStyle" /> - </case> - <default> - <Key - latin:keyLabel="@string/keylabel_for_symbols_comma" - latin:keyWidth="9.2%p" - latin:keyStyle="settingsPopupStyle" /> - </default> - </switch> + <Key + latin:keyLabel="@string/keylabel_for_comma" + latin:keyStyle="f1PopupStyle" /> </default> </switch> </case> - <!-- hasSettingsKey="false" --> + <!-- hasSettingsKey="true" or navigateAction="true" --> <default> <switch> <case @@ -78,37 +63,30 @@ > <Key latin:keyLabel="/" - latin:keyStyle="settingsPopupStyle" /> + latin:keyWidth="9.2%p" + latin:keyStyle="f1PopupStyle" /> </case> <case latin:mode="email" > <Key latin:keyLabel="\@" - latin:keyStyle="settingsPopupStyle" /> + latin:keyWidth="9.2%p" + latin:keyStyle="f1PopupStyle" /> </case> + <case + latin:hasShortcutKey="true" + > + <Key + latin:keyStyle="shortcutKeyStyle" + latin:keyWidth="9.2%p" /> + </case> + <!-- hasShortcutKey="false" --> <default> - <switch> - <case - latin:hasVoiceKey="true" - > - <Key - latin:keyStyle="shortcutKeyStyle" /> - </case> - <!-- latin:hasVoiceKey="false" --> - <case - latin:navigateAction="true" - > - <Key - latin:keyLabel="." - latin:keyStyle="settingsPopupStyle" /> - </case> - <default> - <Key - latin:keyLabel="@string/keylabel_for_symbols_comma" - latin:keyStyle="settingsPopupStyle" /> - </default> - </switch> + <Key + latin:keyLabel="@string/keylabel_for_comma" + latin:keyWidth="9.2%p" + latin:keyStyle="f1PopupStyle" /> </default> </switch> </default> diff --git a/java/res/xml/kbd_qwerty_row4.xml b/java/res/xml/kbd_qwerty_row4.xml index 771333ca9..eb1e9b8b3 100644 --- a/java/res/xml/kbd_qwerty_row4.xml +++ b/java/res/xml/kbd_qwerty_row4.xml @@ -26,46 +26,38 @@ > <switch> <case - latin:hasSettingsKey="true" + latin:hasSettingsKey="false" + latin:navigateAction="false" > <Key latin:keyStyle="toSymbolKeyStyle" - latin:keyWidth="13.75%p" /> - <Key - latin:keyStyle="settingsKeyStyle" - latin:keyWidth="9.2%p" /> + latin:keyWidth="15%p" /> <include latin:keyboardLayout="@xml/kbd_qwerty_f1" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="35.83%p" /> + latin:keyWidth="50%p" /> <Key - latin:keyStyle="punctuationKeyStyle" - latin:keyWidth="9.2%p" /> + latin:keyStyle="punctuationKeyStyle" /> + <Key + latin:keyStyle="returnKeyStyle" + latin:keyWidth="fillRight" /> </case> - <!-- latin:hasSettingsKey="false" --> + <!-- hasSettingsKey="true" or navigateAction="true" --> <default> <Key latin:keyStyle="toSymbolKeyStyle" - latin:keyWidth="15%p" /> + latin:keyWidth="13.75%p" /> + <include + latin:keyboardLayout="@xml/kbd_settings_or_tab" /> <include latin:keyboardLayout="@xml/kbd_qwerty_f1" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="50%p" /> - <Key - latin:keyStyle="punctuationKeyStyle" /> - </default> - </switch> - <switch> - <case - latin:mode="im" - > + latin:keyWidth="35.83%p" /> <Key - latin:keyStyle="smileyKeyStyle" - latin:keyWidth="fillRight" /> - </case> - <default> + latin:keyStyle="punctuationKeyStyle" + latin:keyWidth="9.2%p" /> <Key latin:keyStyle="returnKeyStyle" latin:keyWidth="fillRight" /> diff --git a/java/res/xml/kbd_rows_arabic.xml b/java/res/xml/kbd_rows_arabic.xml index e9de4ad0d..daa2a65ed 100644 --- a/java/res/xml/kbd_rows_arabic.xml +++ b/java/res/xml/kbd_rows_arabic.xml @@ -42,7 +42,7 @@ <Key latin:keyLabel="ف" latin:keyHintLabel="4" - latin:popupCharacters="\u06a4,4,٤" /> + latin:popupCharacters="4,٤,\u06a4" /> <Key latin:keyLabel="غ" latin:keyHintLabel="5" diff --git a/java/res/xml/kbd_rows_russian.xml b/java/res/xml/kbd_rows_russian.xml index 216d74946..3aeb52bcc 100644 --- a/java/res/xml/kbd_rows_russian.xml +++ b/java/res/xml/kbd_rows_russian.xml @@ -69,6 +69,7 @@ latin:popupCharacters="0" /> <Key latin:keyLabel="х" + latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" latin:keyWidth="fillRight" /> </Row> <Row diff --git a/java/res/xml/kbd_rows_symbols.xml b/java/res/xml/kbd_rows_symbols.xml index a8eeb3b91..75e21a002 100644 --- a/java/res/xml/kbd_rows_symbols.xml +++ b/java/res/xml/kbd_rows_symbols.xml @@ -95,7 +95,7 @@ latin:keyWidth="10%p" > <Key - latin:keyStyle="altKeyStyle" + latin:keyStyle="toMoreSymbolKeyStyle" latin:keyWidth="15%p" latin:visualInsetsRight="1%p" /> <Key diff --git a/java/res/xml/kbd_rows_symbols_shift.xml b/java/res/xml/kbd_rows_symbols_shift.xml index 0706cbc78..d52341512 100644 --- a/java/res/xml/kbd_rows_symbols_shift.xml +++ b/java/res/xml/kbd_rows_symbols_shift.xml @@ -91,7 +91,7 @@ latin:keyWidth="10%p" > <Key - latin:keyStyle="altKeyStyle" + latin:keyStyle="backFromMoreSymbolKeyStyle" latin:keyWidth="15%p" latin:visualInsetsRight="1%p" /> <Key diff --git a/java/res/xml/kbd_settings_or_tab.xml b/java/res/xml/kbd_settings_or_tab.xml new file mode 100644 index 000000000..4a8bcc7a6 --- /dev/null +++ b/java/res/xml/kbd_settings_or_tab.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** 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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:hasSettingsKey="true" + > + <Key + latin:keyStyle="settingsKeyStyle" + latin:keyWidth="9.2%p" /> + </case> + <!-- hasSettingsKey="false" --> + <case + latin:navigateAction="true" + > + <Key + latin:keyStyle="tabKeyStyle" + latin:keyWidth="9.2%p" /> + </case> + <!-- hasSettingsKey="false" and navigateAction="false" --> + <default> + <!-- No key. --> + </default> + </switch> +</merge> diff --git a/java/res/xml/kbd_symbols_f1.xml b/java/res/xml/kbd_symbols_f1.xml index cebb4f0b2..0dd3d9109 100644 --- a/java/res/xml/kbd_symbols_f1.xml +++ b/java/res/xml/kbd_symbols_f1.xml @@ -23,39 +23,40 @@ > <switch> <case - latin:hasSettingsKey="true" + latin:hasSettingsKey="false" + latin:navigateAction="false" > <switch> <case - latin:hasVoiceKey="true" + latin:hasShortcutKey="true" > <Key - latin:keyStyle="shortcutKeyStyle" - latin:keyWidth="9.2%p" /> + latin:keyStyle="shortcutKeyStyle" /> </case> - <!-- latin:hasVoiceKey="false" --> + <!-- latin:hasShortcutKey="false" --> <default> <Key - latin:keyLabel="@string/keylabel_for_symbols_f1" - latin:keyWidth="9.2%p" - latin:keyStyle="symbolsF1PopupStyle" /> + latin:keyLabel="@string/keylabel_for_f1" + latin:keyStyle="f1PopupStyle" /> </default> </switch> </case> - <!-- hasSettingsKey="false" --> + <!-- hasSettingsKey="true" or navigateAction="true" --> <default> <switch> <case - latin:hasVoiceKey="true" + latin:hasShortcutKey="true" > <Key - latin:keyStyle="shortcutKeyStyle" /> + latin:keyStyle="shortcutKeyStyle" + latin:keyWidth="9.2%p" /> </case> - <!-- latin:hasVoiceKey="false" --> + <!-- latin:hasShortcutKey="false" --> <default> <Key - latin:keyLabel="@string/keylabel_for_symbols_f1" - latin:keyStyle="symbolsF1PopupStyle" /> + latin:keyLabel="@string/keylabel_for_f1" + latin:keyWidth="9.2%p" + latin:keyStyle="f1PopupStyle" /> </default> </switch> </default> diff --git a/java/res/xml/kbd_symbols_row4.xml b/java/res/xml/kbd_symbols_row4.xml index 3fda8883e..864cf2b8e 100644 --- a/java/res/xml/kbd_symbols_row4.xml +++ b/java/res/xml/kbd_symbols_row4.xml @@ -26,46 +26,38 @@ > <switch> <case - latin:hasSettingsKey="true" + latin:hasSettingsKey="false" + latin:navigateAction="false" > <Key latin:keyStyle="toAlphaKeyStyle" - latin:keyWidth="13.75%p" /> - <Key - latin:keyStyle="settingsKeyStyle" - latin:keyWidth="9.2%p" /> + latin:keyWidth="15%p" /> <include - latin:keyboardLayout="@xml/kbd_qwerty_f1" /> + latin:keyboardLayout="@xml/kbd_symbols_f1" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="35.83%p" /> + latin:keyWidth="50%p" /> <Key - latin:keyStyle="basePunctuationKeyStyle" - latin:keyWidth="9.2%p" /> + latin:keyStyle="punctuationKeyStyle" /> + <Key + latin:keyStyle="returnKeyStyle" + latin:keyWidth="fillRight" /> </case> - <!-- latin:hasSettingsKey="false" --> + <!-- hasSettingsKey="true" or navigateAction="true" --> <default> <Key latin:keyStyle="toAlphaKeyStyle" - latin:keyWidth="15%p" /> + latin:keyWidth="13.75%p" /> <include - latin:keyboardLayout="@xml/kbd_symbols_f1" /> + latin:keyboardLayout="@xml/kbd_settings_or_tab" /> + <include + latin:keyboardLayout="@xml/kbd_qwerty_f1" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="50%p" /> - <Key - latin:keyStyle="basePunctuationKeyStyle" /> - </default> - </switch> - <switch> - <case - latin:mode="im" - > + latin:keyWidth="35.83%p" /> <Key - latin:keyStyle="smileyKeyStyle" - latin:keyWidth="fillRight" /> - </case> - <default> + latin:keyStyle="punctuationKeyStyle" + latin:keyWidth="9.2%p" /> <Key latin:keyStyle="returnKeyStyle" latin:keyWidth="fillRight" /> diff --git a/java/res/xml/kbd_symbols_shift_row4.xml b/java/res/xml/kbd_symbols_shift_row4.xml index 35d444a75..9cb453fbc 100644 --- a/java/res/xml/kbd_symbols_shift_row4.xml +++ b/java/res/xml/kbd_symbols_shift_row4.xml @@ -26,53 +26,45 @@ > <switch> <case - latin:hasSettingsKey="true" + latin:hasSettingsKey="false" + latin:navigateAction="false" > <Key latin:keyStyle="toAlphaKeyStyle" - latin:keyWidth="13.75%p" /> - <Key - latin:keyStyle="settingsKeyStyle" - latin:keyWidth="9.2%p" /> + latin:keyWidth="15%p" /> <Key latin:keyLabel="„" latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" - latin:keyWidth="9.2%p" latin:keyStyle="nonPasswordFunctionalKeyStyle" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="35.83%p" /> + latin:keyWidth="50%p" /> <Key latin:keyLabel="…" - latin:keyWidth="9.2%p" latin:keyStyle="nonPasswordFunctionalKeyStyle" /> + <Key + latin:keyStyle="returnKeyStyle" + latin:keyWidth="fillRight" /> </case> - <!-- latin:hasSettingsKey="false" --> + <!-- hasSettingsKey="true" or navigateAction="true" --> <default> <Key latin:keyStyle="toAlphaKeyStyle" - latin:keyWidth="15%p" /> + latin:keyWidth="13.75%p" /> + <include + latin:keyboardLayout="@xml/kbd_settings_or_tab" /> <Key latin:keyLabel="„" latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" + latin:keyWidth="9.2%p" latin:keyStyle="nonPasswordFunctionalKeyStyle" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="50%p" /> + latin:keyWidth="35.83%p" /> <Key latin:keyLabel="…" + latin:keyWidth="9.2%p" latin:keyStyle="nonPasswordFunctionalKeyStyle" /> - </default> - </switch> - <switch> - <case - latin:mode="im" - > - <Key - latin:keyStyle="smileyKeyStyle" - latin:keyWidth="fillRight" /> - </case> - <default> <Key latin:keyStyle="returnKeyStyle" latin:keyWidth="fillRight" /> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 7a4e7ea35..0bf560d5a 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -3,16 +3,16 @@ /** * 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 + * 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 + * 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 + * 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. */ --> @@ -20,10 +20,8 @@ <!-- The attributes in this XML file provide configuration information --> <!-- for the Input Method Manager. --> -<!-- Keyboard: en_US, en_GB, ar, cs, da, de, es, es_US, fi, fr, fr_CA, fr_CH, hr, hu, it, iw, nb, nl, pl, pt, ru, sr, sv, tr --> -<!-- Voice: af, cs, da, de, en, es, fr, it, ja, ko, nl, pl, pt, ru, tr, yue, zh, zu --> +<!-- Keyboard: en_US, en_GB, ar, cs, da, de, de_ZZ, es, es_US, fi, fr, fr_CA, fr_CH, hr, hu, it, iw, nb, nl, pl, pt, ru, sr, sv, tr --> <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. --> -<!-- TODO: use <lang>_mic icon instead of a common mic icon. --> <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default subtype.--> <input-method xmlns:android="http://schemas.android.com/apk/res/android" diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index babbfaa8c..6d2218d2f 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -48,14 +48,12 @@ android:title="@string/prefs_enable_recorrection" android:summary="@string/prefs_enable_recorrection_summary" android:persistent="true" - android:defaultValue="@bool/config_default_recorrection_enabled" /> - <ListPreference - android:key="settings_key" + android:defaultValue="@bool/config_default_compat_recorrection_enabled" /> + <CheckBoxPreference + android:key="show_settings_key" android:title="@string/prefs_settings_key" android:persistent="true" - android:entryValues="@array/settings_key_modes_values" - android:entries="@array/settings_key_modes" - android:defaultValue="@string/settings_key_mode_auto" /> + android:defaultValue="@bool/config_default_show_settings_key" /> <ListPreference android:key="voice_mode" android:title="@string/voice_input" @@ -72,8 +70,8 @@ android:title="@string/configure_dictionaries_title"> <intent android:action="android.intent.action.MAIN" - android:targetPackage="com.google.android.inputmethod.latin.dictionarypack" - android:targetClass="com.google.android.inputmethod.latin.dictionarypack.DictionarySettings" /> + android:targetPackage="@string/dictionary_pack_package_name" + android:targetClass="@string/dictionary_pack_settings_activity" /> </PreferenceScreen> <ListPreference android:key="auto_correction_threshold" diff --git a/java/res/xml/spell_checker_settings.xml b/java/res/xml/spell_checker_settings.xml new file mode 100644 index 000000000..f402555c9 --- /dev/null +++ b/java/res/xml/spell_checker_settings.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + android:title="@string/android_spell_checker_settings"> + <CheckBoxPreference + android:key="use_proximity" + android:title="@string/use_proximity_option_title" + android:summary="@string/use_proximity_option_summary" + android:persistent="true" + android:defaultValue="true" /> +</PreferenceScreen> diff --git a/java/res/xml/spellchecker.xml b/java/res/xml/spellchecker.xml new file mode 100644 index 000000000..ce09264ce --- /dev/null +++ b/java/res/xml/spellchecker.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> + +<!-- The attributes in this XML file provide the configuration information --> +<!-- for the spell checker --> + +<spell-checker xmlns:android="http://schemas.android.com/apk/res/android" + android:label="@string/spell_checker_service_name"> + <subtype + android:label="@string/subtype_en_US" + android:subtypeLocale="en_US" + /> + <subtype + android:label="@string/subtype_en_GB" + android:subtypeLocale="en_GB" + /> +</spell-checker> diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 1619451f0..3dca9aae6 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -149,15 +149,6 @@ public class AccessibleKeyboardViewProxy { return onHoverEventInternal(event, tracker); } - public boolean dispatchTouchEvent(MotionEvent event) { - // Since touch exploration translates hover double-tap to a regular - // single-tap, we're going to drop non-touch exploration events. - if (!AccessibilityUtils.getInstance().isTouchExplorationEvent(event)) - return true; - - return false; - } - /** * Handles touch exploration events when Accessibility is turned on. * diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java index cf6cd0f5e..e75559e62 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java @@ -16,7 +16,7 @@ package com.android.inputmethod.deprecated.languageswitcher; -import com.android.inputmethod.keyboard.internal.KeyboardParser; +import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.latin.DictionaryFactory; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; @@ -162,7 +162,7 @@ public class InputLanguageSelection extends PreferenceActivity { try { final String localeStr = locale.toString(); - final String[] layoutCountryCodes = KeyboardParser.parseKeyboardLocale( + final String[] layoutCountryCodes = KeyboardBuilder.parseKeyboardLocale( this, R.xml.kbd_qwerty).split(",", -1); if (!TextUtils.isEmpty(localeStr) && layoutCountryCodes.length > 0) { for (String s : layoutCountryCodes) { diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java index d40728d25..bf2512d7b 100644 --- a/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java @@ -271,9 +271,10 @@ public class Recorrection implements SharedPreferences.OnSharedPreferenceChangeL // but always use the default setting defined in the resources. if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) { mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED, - res.getBoolean(R.bool.config_default_recorrection_enabled)); + res.getBoolean(R.bool.config_default_compat_recorrection_enabled)); } else { - mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled); + mRecorrectionEnabled = + res.getBoolean(R.bool.config_default_compat_recorrection_enabled); } } diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java index 5e6c87044..f33a46277 100644 --- a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java @@ -57,6 +57,7 @@ public class RecorrectionSuggestionEntries { private static SuggestedWords.Builder getTypedSuggestions( Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) { - return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null); + return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null, + keyboardSwitcher.getLatinKeyboard().getProximityInfo()); } } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 0ee8d7174..397b7b16b 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -27,13 +27,13 @@ import android.util.Xml; import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; -import com.android.inputmethod.keyboard.internal.KeyboardParser; -import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; +import com.android.inputmethod.keyboard.internal.KeyboardParams; +import com.android.inputmethod.keyboard.internal.KeyboardBuilder; +import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException; import com.android.inputmethod.keyboard.internal.PopupCharactersParser; import com.android.inputmethod.keyboard.internal.Row; import com.android.inputmethod.latin.R; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -51,10 +51,10 @@ public class Key { /** Hint label to display on the key in conjunction with the label */ public final CharSequence mHintLabel; /** Option of the label */ - public final int mLabelOption; - public static final int LABEL_OPTION_ALIGN_LEFT = 0x01; - public static final int LABEL_OPTION_ALIGN_RIGHT = 0x02; - public static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08; + private final int mLabelOption; + private static final int LABEL_OPTION_ALIGN_LEFT = 0x01; + private static final int LABEL_OPTION_ALIGN_RIGHT = 0x02; + private static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08; private static final int LABEL_OPTION_LARGE_LETTER = 0x10; private static final int LABEL_OPTION_FONT_NORMAL = 0x20; private static final int LABEL_OPTION_FONT_MONO_SPACE = 0x40; @@ -63,6 +63,8 @@ public class Key { private static final int LABEL_OPTION_HAS_POPUP_HINT = 0x200; private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x400; private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800; + private static final int LABEL_OPTION_WITH_ICON_LEFT = 0x1000; + private static final int LABEL_OPTION_WITH_ICON_RIGHT = 0x2000; /** Icon to display instead of a label. Icon takes precedence over a label */ private Drawable mIcon; @@ -74,7 +76,9 @@ public class Key { /** Height of the key, not including the gap */ public final int mHeight; /** The horizontal gap around this key */ - public final int mGap; + public final int mHorizontalGap; + /** The vertical gap below this key */ + public final int mVerticalGap; /** The visual insets */ public final int mVisualInsetsLeft; public final int mVisualInsetsRight; @@ -103,15 +107,14 @@ public class Key { /** Whether this key repeats itself when held down */ public final boolean mRepeatable; - /** The Keyboard that this key belongs to */ - private final Keyboard mKeyboard; - /** The current pressed state of this key */ private boolean mPressed; /** If this is a sticky key, is its highlight on? */ private boolean mHighlightOn; /** Key is enabled and responds on press */ private boolean mEnabled = true; + /** Whether this key needs to show the "..." popup hint for special purposes */ + private boolean mNeedsSpecialPopupHint; // keyWidth constants private static final int KEYWIDTH_FILL_RIGHT = 0; @@ -192,13 +195,13 @@ public class Key { /** * This constructor is being used only for key in popup mini keyboard. */ - public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y, + public Key(Resources res, KeyboardParams params, CharSequence popupCharacter, int x, int y, int width, int height, int edgeFlags) { - mKeyboard = keyboard; - mHeight = height - keyboard.getVerticalGap(); - mGap = keyboard.getHorizontalGap(); + mHeight = height - params.mVerticalGap; + mHorizontalGap = params.mHorizontalGap; + mVerticalGap = params.mVerticalGap; mVisualInsetsLeft = mVisualInsetsRight = 0; - mWidth = width - mGap; + mWidth = width - mHorizontalGap; mEdgeFlags = edgeFlags; mHintLabel = null; mLabelOption = 0; @@ -211,10 +214,10 @@ public class Key { mLabel = PopupCharactersParser.getLabel(popupSpecification); mOutputText = PopupCharactersParser.getOutputText(popupSpecification); final int code = PopupCharactersParser.getCode(res, popupSpecification); - mCode = keyboard.isRtlKeyboard() ? getRtlParenthesisCode(code) : code; - mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification)); + mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(code) : code; + mIcon = params.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification)); // Horizontal gap is divided equally to both sides of the key. - mX = x + mGap / 2; + mX = x + mHorizontalGap / 2; mY = y; } @@ -222,30 +225,30 @@ public class Key { * Create a key with the given top-left coordinate and extract its attributes from the XML * parser. * @param res resources associated with the caller's context - * @param row the row that this key belongs to. The row must already be attached to - * a {@link Keyboard}. + * @param params the keyboard building parameters. + * @param row the row that this key belongs to. * @param x the x coordinate of the top-left * @param y the y coordinate of the top-left * @param parser the XML parser containing the attributes for this key * @param keyStyles active key styles set */ - public Key(Resources res, Row row, int x, int y, XmlResourceParser parser, - KeyStyles keyStyles) { - mKeyboard = row.getKeyboard(); + public Key(Resources res, KeyboardParams params, Row row, int x, int y, + XmlResourceParser parser, KeyStyles keyStyles) { final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); int keyWidth; try { - mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr, + mHeight = KeyboardBuilder.getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_rowHeight, - mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap; - mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr, + params.mHeight, row.mRowHeight) - params.mVerticalGap; + mHorizontalGap = KeyboardBuilder.getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_horizontalGap, - mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap); - keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr, + params.mWidth, params.mHorizontalGap); + mVerticalGap = params.mVerticalGap; + keyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth, - mKeyboard.getDisplayWidth(), row.mDefaultWidth); + params.mWidth, row.mDefaultKeyWidth); } finally { keyboardAttr.recycle(); } @@ -263,8 +266,8 @@ public class Key { style = keyStyles.getEmptyKeyStyle(); } - final int keyboardWidth = mKeyboard.getDisplayWidth(); - int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, + final int keyboardWidth = params.mOccupiedWidth; + int keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x); if (keyXPos < 0) { // If keyXPos is negative, the actual x-coordinate will be k + keyXPos. @@ -288,23 +291,28 @@ public class Key { } // Horizontal gap is divided equally to both sides of the key. - mX = keyXPos + mGap / 2; + mX = keyXPos + mHorizontalGap / 2; mY = y; - mWidth = keyWidth - mGap; + mWidth = keyWidth - mHorizontalGap; - final CharSequence[] popupCharacters = style.getTextArray(keyAttr, - R.styleable.Keyboard_Key_popupCharacters); + CharSequence[] popupCharacters = style.getTextArray( + keyAttr, R.styleable.Keyboard_Key_popupCharacters); + if (params.mId.mPasswordInput) { + popupCharacters = PopupCharactersParser.filterOut( + res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER); + } // In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of // config_digit_popup_characters_enabled. - if (mKeyboard.mId.isAlphabetKeyboard() && !res.getBoolean( + if (params.mId.isAlphabetKeyboard() && !res.getBoolean( R.bool.config_digit_popup_characters_enabled)) { - mPopupCharacters = filterOutDigitPopupCharacters(popupCharacters); + mPopupCharacters = PopupCharactersParser.filterOut( + res, popupCharacters, PopupCharactersParser.DIGIT_FILTER); } else { mPopupCharacters = popupCharacters; } mMaxPopupColumn = style.getInt(keyboardAttr, R.styleable.Keyboard_Key_maxPopupKeyboardColumn, - mKeyboard.getMaxPopupKeyboardColumn()); + params.mMaxPopupColumn); mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false); @@ -312,19 +320,23 @@ public class Key { mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); mEdgeFlags = 0; - final KeyboardIconsSet iconsSet = mKeyboard.mIconsSet; - mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr, + final KeyboardIconsSet iconsSet = params.mIconsSet; + mVisualInsetsLeft = KeyboardBuilder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0); - mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr, + mVisualInsetsRight = KeyboardBuilder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsRight, keyboardWidth, 0); mPreviewIcon = iconsSet.getIcon(style.getInt( keyAttr, R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED)); - Keyboard.setDefaultBounds(mPreviewIcon); mIcon = iconsSet.getIcon(style.getInt( keyAttr, R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED)); - Keyboard.setDefaultBounds(mIcon); + final int shiftedIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted, + KeyboardIconsSet.ICON_UNDEFINED); + if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) { + final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId); + params.addShiftedIcon(this, shiftedIcon); + } mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); @@ -336,18 +348,12 @@ public class Key { Keyboard.CODE_UNSPECIFIED); if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) { final int firstChar = mLabel.charAt(0); - mCode = mKeyboard.isRtlKeyboard() ? getRtlParenthesisCode(firstChar) : firstChar; + mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(firstChar) : firstChar; } else if (code != Keyboard.CODE_UNSPECIFIED) { mCode = code; } else { mCode = Keyboard.CODE_DUMMY; } - - final Drawable shiftedIcon = iconsSet.getIcon(style.getInt( - keyAttr, R.styleable.Keyboard_Key_keyIconShifted, - KeyboardIconsSet.ICON_UNDEFINED)); - if (shiftedIcon != null) - mKeyboard.getShiftedIcons().put(this, shiftedIcon); } finally { keyAttr.recycle(); } @@ -357,10 +363,6 @@ public class Key { mEdgeFlags |= flags; } - public CharSequence getCaseAdjustedLabel() { - return mKeyboard.adjustLabelCase(mLabel); - } - public Typeface selectTypeface(Typeface defaultTypeface) { // TODO: Handle "bold" here too? if ((mLabelOption & LABEL_OPTION_FONT_NORMAL) != 0) { @@ -386,10 +388,30 @@ public class Key { } } + public boolean isAlignLeft() { + return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0; + } + + public boolean isAlignRight() { + return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0; + } + + public boolean isAlignLeftOfCenter() { + return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0; + } + public boolean hasPopupHint() { return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0; } + public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) { + mNeedsSpecialPopupHint = needsSpecialPopupHint; + } + + public boolean needsSpecialPopupHint() { + return mNeedsSpecialPopupHint; + } + public boolean hasUppercaseLetter() { return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0; } @@ -398,34 +420,12 @@ public class Key { return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0; } - private static boolean isDigitPopupCharacter(CharSequence label) { - return label != null && label.length() == 1 && Character.isDigit(label.charAt(0)); + public boolean hasLabelWithIconLeft() { + return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0; } - private static CharSequence[] filterOutDigitPopupCharacters(CharSequence[] popupCharacters) { - if (popupCharacters == null || popupCharacters.length < 1) - return null; - if (popupCharacters.length == 1 && isDigitPopupCharacter( - PopupCharactersParser.getLabel(popupCharacters[0].toString()))) - return null; - ArrayList<CharSequence> filtered = null; - for (int i = 0; i < popupCharacters.length; i++) { - final CharSequence popupSpec = popupCharacters[i]; - if (isDigitPopupCharacter(PopupCharactersParser.getLabel(popupSpec.toString()))) { - if (filtered == null) { - filtered = new ArrayList<CharSequence>(); - for (int j = 0; j < i; j++) - filtered.add(popupCharacters[j]); - } - } else if (filtered != null) { - filtered.add(popupSpec); - } - } - if (filtered == null) - return popupCharacters; - if (filtered.size() == 0) - return null; - return filtered.toArray(new CharSequence[filtered.size()]); + public boolean hasLabelWithIconRight() { + return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0; } public Drawable getIcon() { @@ -482,10 +482,10 @@ public class Key { * assume that all points between the key and the edge are considered to be on the key. */ public boolean isOnKey(int x, int y) { - final int left = mX - mGap / 2; - final int right = left + mWidth + mGap; + final int left = mX - mHorizontalGap / 2; + final int right = left + mWidth + mHorizontalGap; final int top = mY; - final int bottom = top + mHeight + mKeyboard.getVerticalGap(); + final int bottom = top + mHeight + mVerticalGap; final int flags = mEdgeFlags; if (flags == 0) { return x >= left && x <= right && y >= top && y <= bottom; diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 6d25025c5..0a3acb48b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -57,7 +57,7 @@ public class KeyDetector { mCorrectionX = (int)correctionX; mCorrectionY = (int)correctionY; mKeyboard = keyboard; - final int threshold = keyboard.getMostCommonKeyWidth(); + final int threshold = keyboard.mMostCommonKeyWidth; mProximityThresholdSquare = threshold * threshold; } @@ -153,7 +153,7 @@ public class KeyDetector { } private void getNearbyKeyCodes(final int[] allCodes) { - final List<Key> keys = getKeyboard().getKeys(); + final List<Key> keys = getKeyboard().mKeys; final int[] indices = mIndices; // allCodes[0] should always have the key code even if it is a non-letter key. @@ -187,7 +187,7 @@ public class KeyDetector { * @return The nearest key index */ public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) { - final List<Key> keys = getKeyboard().getKeys(); + final List<Key> keys = getKeyboard().mKeys; final int touchX = getTouchX(x); final int touchY = getTouchY(y); diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 19847c5ec..f8e08b06a 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -16,25 +16,17 @@ package com.android.inputmethod.keyboard; -import android.content.Context; -import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.text.TextUtils; -import android.util.Log; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; -import com.android.inputmethod.keyboard.internal.KeyboardParser; +import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.KeyboardShiftState; -import com.android.inputmethod.latin.R; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; /** * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard @@ -55,8 +47,6 @@ import java.util.Map; * </pre> */ public class Keyboard { - private static final String TAG = Keyboard.class.getSimpleName(); - public static final int EDGE_LEFT = 0x01; public static final int EDGE_RIGHT = 0x02; public static final int EDGE_TOP = 0x04; @@ -77,6 +67,8 @@ public class Keyboard { public static final int CODE_CLOSING_SQUARE_BRACKET = ']'; public static final int CODE_CLOSING_CURLY_BRACKET = '}'; public static final int CODE_CLOSING_ANGLE_BRACKET = '>'; + public static final int CODE_DIGIT0 = '0'; + public static final int CODE_PLUS = '+'; /** Special keys code. These should be aligned with values/keycodes.xml */ @@ -87,227 +79,94 @@ public class Keyboard { public static final int CODE_CANCEL = -4; public static final int CODE_DELETE = -5; public static final int CODE_SETTINGS = -6; - public static final int CODE_SETTINGS_LONGPRESS = -7; - public static final int CODE_SHORTCUT = -8; + public static final int CODE_SHORTCUT = -7; // Code value representing the code is not specified. public static final int CODE_UNSPECIFIED = -99; - /** Horizontal gap default for all rows */ - private int mDefaultHorizontalGap; + public final KeyboardId mId; + + /** Total height of the keyboard, including the padding and keys */ + public final int mOccupiedHeight; + /** Total width of the keyboard, including the padding and keys */ + public final int mOccupiedWidth; - /** Default key width */ - private int mDefaultWidth; + public final int mHeight; + public final int mWidth; - /** Default key height */ - private int mDefaultHeight; + /** Default row height */ + public final int mDefaultRowHeight; /** Default gap between rows */ - private int mDefaultVerticalGap; + public final int mVerticalGap; + + public final int mMostCommonKeyWidth; /** Popup keyboard template */ - private int mPopupKeyboardResId; + public final int mPopupKeyboardResId; /** Maximum column for popup keyboard */ - private int mMaxPopupColumn; + public final int mMaxPopupColumn; /** True if Right-To-Left keyboard */ - private boolean mIsRtlKeyboard; + public final boolean mIsRtlKeyboard; - /** List of shift keys in this keyboard and its icons and state */ - private final List<Key> mShiftKeys = new ArrayList<Key>(); - private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>(); - private final HashMap<Key, Drawable> mNormalShiftIcons = new HashMap<Key, Drawable>(); - private final HashSet<Key> mShiftLockEnabled = new HashSet<Key>(); - private final KeyboardShiftState mShiftState = new KeyboardShiftState(); - - /** Total height of the keyboard, including the padding and keys */ - private int mTotalHeight; - - /** - * Total width (minimum width) of the keyboard, including left side gaps and keys, but not any - * gaps on the right side. - */ - private int mMinWidth; + /** List of keys and icons in this keyboard */ + public final List<Key> mKeys; + public final List<Key> mShiftKeys; + public final Set<Key> mShiftLockKeys; + public final Map<Key, Drawable> mShiftedIcons; + public final Map<Key, Drawable> mUnshiftedIcons; + public final KeyboardIconsSet mIconsSet; - /** List of keys in this keyboard */ - private final List<Key> mKeys = new ArrayList<Key>(); - - /** Width of the screen available to fit the keyboard */ - private final int mDisplayWidth; - - /** Height of the screen */ - private final int mDisplayHeight; - - /** Height of keyboard */ - private int mKeyboardHeight; - - private int mMostCommonKeyWidth = 0; - - public final KeyboardId mId; - - public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); - - // Variables for pre-computing nearest keys. - - // TODO: Change GRID_WIDTH and GRID_HEIGHT to private. - public final int GRID_WIDTH; - public final int GRID_HEIGHT; + private final KeyboardShiftState mShiftState = new KeyboardShiftState(); private final ProximityInfo mProximityInfo; - /** - * Creates a keyboard from the given xml key layout file. - * @param context the application or service context - * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. - * @param id keyboard identifier - * @param width keyboard width - */ + public Keyboard(KeyboardParams params) { + mId = params.mId; + mOccupiedHeight = params.mOccupiedHeight; + mOccupiedWidth = params.mOccupiedWidth; + mHeight = params.mHeight; + mWidth = params.mWidth; + mMostCommonKeyWidth = params.mMostCommonKeyWidth; + mIsRtlKeyboard = params.mIsRtlKeyboard; + mPopupKeyboardResId = params.mPopupKeyboardResId; + mMaxPopupColumn = params.mMaxPopupColumn; + + mDefaultRowHeight = params.mDefaultRowHeight; + mVerticalGap = params.mVerticalGap; + + mKeys = Collections.unmodifiableList(params.mKeys); + mShiftKeys = Collections.unmodifiableList(params.mShiftKeys); + mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys); + mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons); + mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons); + mIconsSet = params.mIconsSet; - public Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width) { - final Resources res = context.getResources(); - GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); - GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); - - final int horizontalEdgesPadding = (int)res.getDimension( - R.dimen.keyboard_horizontal_edges_padding); - mDisplayWidth = width - horizontalEdgesPadding * 2; - // TODO: Adjust the height by referring to the height of area available for drawing as well. - mDisplayHeight = res.getDisplayMetrics().heightPixels; - - mDefaultHorizontalGap = 0; - setKeyWidth(mDisplayWidth / 10); - mDefaultVerticalGap = 0; - mDefaultHeight = mDefaultWidth; - mId = id; - loadKeyboard(context, xmlLayoutResId); mProximityInfo = new ProximityInfo( - GRID_WIDTH, GRID_HEIGHT, getMinWidth(), getHeight(), getKeyWidth(), mKeys); - } - - public int getProximityInfo() { - return mProximityInfo.getNativeProximityInfo(); - } - - public List<Key> getKeys() { - return mKeys; - } - - public int getHorizontalGap() { - return mDefaultHorizontalGap; - } - - public void setHorizontalGap(int gap) { - mDefaultHorizontalGap = gap; + params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight, + mMostCommonKeyWidth, mKeys); } - public int getVerticalGap() { - return mDefaultVerticalGap; - } - - public void setVerticalGap(int gap) { - mDefaultVerticalGap = gap; - } - - public int getRowHeight() { - return mDefaultHeight; - } - - public void setRowHeight(int height) { - mDefaultHeight = height; - } - - public int getKeyWidth() { - return mDefaultWidth; - } - - public void setKeyWidth(int width) { - mDefaultWidth = width; - } - - /** - * Returns the total height of the keyboard - * @return the total height of the keyboard - */ - public int getHeight() { - return mTotalHeight; + public ProximityInfo getProximityInfo() { + return mProximityInfo; } - public void setHeight(int height) { - mTotalHeight = height; - } - - public int getMinWidth() { - return mMinWidth; - } - - public void setMinWidth(int minWidth) { - mMinWidth = minWidth; - } - - public int getDisplayHeight() { - return mDisplayHeight; - } - - public int getDisplayWidth() { - return mDisplayWidth; - } - - public int getKeyboardHeight() { - return mKeyboardHeight; - } - - public void setKeyboardHeight(int height) { - mKeyboardHeight = height; - } - - public boolean isRtlKeyboard() { - return mIsRtlKeyboard; - } - - public void setRtlKeyboard(boolean isRtl) { - mIsRtlKeyboard = isRtl; - } - - public int getPopupKeyboardResId() { - return mPopupKeyboardResId; - } - - public void setPopupKeyboardResId(int resId) { - mPopupKeyboardResId = resId; - } - - public int getMaxPopupKeyboardColumn() { - return mMaxPopupColumn; - } - - public void setMaxPopupKeyboardColumn(int column) { - mMaxPopupColumn = column; - } - - public List<Key> getShiftKeys() { - return mShiftKeys; - } - - public Map<Key, Drawable> getShiftedIcons() { - return mShiftedIcons; - } - - public void enableShiftLock() { - for (final Key key : getShiftKeys()) { - mShiftLockEnabled.add(key); - mNormalShiftIcons.put(key, key.getIcon()); - } - } - - public boolean isShiftLockEnabled(Key key) { - return mShiftLockEnabled.contains(key); + public boolean hasShiftLockKey() { + return !mShiftLockKeys.isEmpty(); } public boolean setShiftLocked(boolean newShiftLockState) { - final Map<Key, Drawable> shiftedIcons = getShiftedIcons(); - for (final Key key : getShiftKeys()) { + for (final Key key : mShiftLockKeys) { + // To represent "shift locked" state. The highlight is handled by background image that + // might be a StateListDrawable. key.setHighlightOn(newShiftLockState); - key.setIcon(newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key)); + // To represent "shifted" state. The key might have a shifted icon. + if (newShiftLockState && mShiftedIcons.containsKey(key)) { + key.setIcon(mShiftedIcons.get(key)); + } else { + key.setIcon(mUnshiftedIcons.get(key)); + } } mShiftState.setShiftLocked(newShiftLockState); return true; @@ -318,12 +177,11 @@ public class Keyboard { } public boolean setShifted(boolean newShiftState) { - final Map<Key, Drawable> shiftedIcons = getShiftedIcons(); - for (final Key key : getShiftKeys()) { + for (final Key key : mShiftKeys) { if (!newShiftState && !mShiftState.isShiftLocked()) { - key.setIcon(mNormalShiftIcons.get(key)); + key.setIcon(mUnshiftedIcons.get(key)); } else if (newShiftState && !mShiftState.isShiftedOrShiftLocked()) { - key.setIcon(shiftedIcons.get(key)); + key.setIcon(mShiftedIcons.get(key)); } } return mShiftState.setShifted(newShiftState); @@ -384,52 +242,4 @@ public class Keyboard { public int[] getNearestKeys(int x, int y) { return mProximityInfo.getNearestKeys(x, y); } - - /** - * Compute the most common key width in order to use it as proximity key detection threshold. - * - * @return The most common key width in the keyboard - */ - public int getMostCommonKeyWidth() { - if (mMostCommonKeyWidth == 0) { - final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>(); - int maxCount = 0; - int mostCommonWidth = 0; - for (final Key key : mKeys) { - final Integer width = key.mWidth + key.mGap; - Integer count = histogram.get(width); - if (count == null) - count = 0; - histogram.put(width, ++count); - if (count > maxCount) { - maxCount = count; - mostCommonWidth = width; - } - } - mMostCommonKeyWidth = mostCommonWidth; - } - return mMostCommonKeyWidth; - } - - private void loadKeyboard(Context context, int xmlLayoutResId) { - try { - KeyboardParser parser = new KeyboardParser(this, context); - parser.parseKeyboard(xmlLayoutResId); - // mMinWidth is the width of this keyboard which is maximum width of row. - mMinWidth = parser.getMaxRowWidth(); - mTotalHeight = parser.getTotalHeight(); - } catch (XmlPullParserException e) { - Log.w(TAG, "keyboard XML parse error: " + e); - throw new IllegalArgumentException(e); - } catch (IOException e) { - Log.w(TAG, "keyboard XML parse error: " + e); - throw new RuntimeException(e); - } - } - - public static void setDefaultBounds(Drawable drawable) { - if (drawable != null) - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight()); - } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 905f779c0..864091289 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -70,4 +70,10 @@ public interface KeyboardActionListener { * Called when user released a finger outside any key. */ public void onCancelInput(); + + /** + * Send a non-"code input" custom request to the listener. + * @return true if the request has been consumed, false otherwise. + */ + public boolean onCustomRequest(int requestCode); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 3f30165aa..d0a2f864c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -42,8 +42,6 @@ public class KeyboardId { public static final int F2KEY_MODE_SHORTCUT_IME = 2; public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3; - private static final int MINI_KEYBOARD_ID_MARKER = -1; - public final Locale mLocale; public final int mOrientation; public final int mWidth; @@ -55,10 +53,9 @@ public class KeyboardId { public final boolean mHasSettingsKey; public final int mF2KeyMode; public final boolean mClobberSettingsKey; - public final boolean mVoiceKeyEnabled; - public final boolean mHasVoiceKey; + public final boolean mShortcutKeyEnabled; + public final boolean mHasShortcutKey; public final int mImeAction; - public final boolean mEnableShiftLock; public final String mXmlName; public final EditorInfo mAttribute; @@ -67,8 +64,7 @@ public class KeyboardId { public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int width, int mode, EditorInfo attribute, boolean hasSettingsKey, int f2KeyMode, - boolean clobberSettingsKey, boolean voiceKeyEnabled, boolean hasVoiceKey, - boolean enableShiftLock) { + boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { final int inputType = (attribute != null) ? attribute.inputType : 0; final int imeOptions = (attribute != null) ? attribute.imeOptions : 0; this.mLocale = locale; @@ -85,13 +81,12 @@ public class KeyboardId { this.mHasSettingsKey = hasSettingsKey; this.mF2KeyMode = f2KeyMode; this.mClobberSettingsKey = clobberSettingsKey; - this.mVoiceKeyEnabled = voiceKeyEnabled; - this.mHasVoiceKey = hasVoiceKey; + this.mShortcutKeyEnabled = shortcutKeyEnabled; + this.mHasShortcutKey = hasShortcutKey; // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and // {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}. this.mImeAction = imeOptions & ( EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); - this.mEnableShiftLock = enableShiftLock; this.mXmlName = xmlName; this.mAttribute = attribute; @@ -107,34 +102,29 @@ public class KeyboardId { hasSettingsKey, f2KeyMode, clobberSettingsKey, - voiceKeyEnabled, - hasVoiceKey, + shortcutKeyEnabled, + hasShortcutKey, mImeAction, - enableShiftLock, }); } - public KeyboardId cloneAsMiniKeyboard() { - return new KeyboardId("mini popup keyboard", MINI_KEYBOARD_ID_MARKER, mLocale, mOrientation, - mWidth, mMode, mAttribute, false, F2KEY_MODE_NONE, false, false, false, false); + public KeyboardId cloneWithNewXml(String xmlName, int xmlId) { + return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute, + false, F2KEY_MODE_NONE, false, false, false); } public KeyboardId cloneWithNewGeometry(int orientation, int width) { if (mWidth == width) return this; return new KeyboardId(mXmlName, mXmlId, mLocale, orientation, width, mMode, mAttribute, - mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey, - mEnableShiftLock); + mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mShortcutKeyEnabled, + mHasShortcutKey); } public int getXmlId() { return mXmlId; } - public boolean isMiniKeyboard() { - return mXmlId == MINI_KEYBOARD_ID_MARKER; - } - public boolean isAlphabetKeyboard() { return mXmlId == R.xml.kbd_qwerty; } @@ -160,7 +150,7 @@ public class KeyboardId { return other instanceof KeyboardId && equals((KeyboardId) other); } - boolean equals(KeyboardId other) { + private boolean equals(KeyboardId other) { return other.mLocale.equals(this.mLocale) && other.mOrientation == this.mOrientation && other.mWidth == this.mWidth @@ -171,10 +161,9 @@ public class KeyboardId { && other.mHasSettingsKey == this.mHasSettingsKey && other.mF2KeyMode == this.mF2KeyMode && other.mClobberSettingsKey == this.mClobberSettingsKey - && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled - && other.mHasVoiceKey == this.mHasVoiceKey - && other.mImeAction == this.mImeAction - && other.mEnableShiftLock == this.mEnableShiftLock; + && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled + && other.mHasShortcutKey == this.mHasShortcutKey + && other.mImeAction == this.mImeAction; } @Override @@ -184,7 +173,7 @@ public class KeyboardId { @Override public String toString() { - return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s%s%s]", + return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s%s]", mXmlName, mLocale, (mOrientation == 1 ? "port" : "land"), mWidth, @@ -195,9 +184,8 @@ public class KeyboardId { (mNavigateAction ? " navigateAction" : ""), (mPasswordInput ? " passwordInput" : ""), (mHasSettingsKey ? " hasSettingsKey" : ""), - (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""), - (mHasVoiceKey ? " hasVoiceKey" : ""), - (mEnableShiftLock ? " enableShiftLock" : "") + (mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""), + (mHasShortcutKey ? " hasShortcutKey" : "") ); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 552a3cd30..21477a992 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -18,7 +18,9 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.content.res.Resources; +import android.inputmethodservice.InputMethodService; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.InflateException; @@ -27,7 +29,6 @@ import android.view.View; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; -import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.keyboard.internal.ModifierKeyState; import com.android.inputmethod.keyboard.internal.ShiftKeyState; import com.android.inputmethod.latin.LatinIME; @@ -38,6 +39,7 @@ import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; import java.lang.ref.SoftReference; +import java.util.Arrays; import java.util.HashMap; import java.util.Locale; @@ -62,6 +64,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private View mCurrentInputView; private LatinKeyboardView mKeyboardView; private LatinIME mInputMethodService; + private String mPackageName; + private Resources mResources; // TODO: Combine these key state objects with auto mode switch state. private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); @@ -74,6 +78,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private KeyboardId mCurrentId; private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); + // TODO: Remove this cache object when {@link DisplayMetrics} has actual window width excluding + // system navigation bar. + private WindowWidthCache mWindowWidthCache; + + private KeyboardLayoutState mSavedKeyboardState = new KeyboardLayoutState(); /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of * what user actually typed. */ @@ -91,20 +100,135 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private static final int SWITCH_STATE_CHORDING_SYMBOL = 6; private int mSwitchState = SWITCH_STATE_ALPHA; - private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto; - private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW = - R.string.settings_key_mode_always_show; - // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to - // in the source code now. - // Default is SETTINGS_KEY_MODE_AUTO. - private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO; - private int mThemeIndex = -1; private Context mThemeContext; - private int mWindowWidth; private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); + private static class WindowWidthCache { + private final InputMethodService mService; + private final Resources mResources; + private final boolean mIsRegistered[] = new boolean[Configuration.ORIENTATION_SQUARE + 1]; + private final int mWidth[] = new int[Configuration.ORIENTATION_SQUARE + 1]; + + public WindowWidthCache(InputMethodService service) { + mService = service; + mResources = service.getResources(); + + Arrays.fill(mIsRegistered, false); + Arrays.fill(mWidth, 0); + } + + private int getCurrentWindowWidth() { + return mService.getWindow().getWindow().getDecorView().getWidth(); + } + + public int getWidth(Configuration conf) { + final int orientation = conf.orientation; + try { + final int width = mWidth[orientation]; + if (mIsRegistered[orientation] || width > 0) { + // Return registered or cached window width for this orientation. + return width; + } + // Fall through + } catch (IndexOutOfBoundsException e) { + Log.w(TAG, "unknwon orientation value " + orientation); + // Fall through + } + + // Return screen width as default window width. + return mResources.getDisplayMetrics().widthPixels; + } + + public int getWidthOnSizeChanged(Configuration conf) { + final int orientation = conf.orientation; + try { + if (mIsRegistered[orientation]) { + // Return registered window width for this orientation. + return mWidth[orientation]; + } + + // Cache the current window width without registering. + final int width = getCurrentWindowWidth(); + mWidth[orientation] = width; + return width; + } catch (IndexOutOfBoundsException e) { + Log.w(TAG, "unknwon orientation value " + orientation); + return 0; + } + } + + public void registerWidth() { + final int orientation = mResources.getConfiguration().orientation; + try { + if (!mIsRegistered[orientation]) { + final int width = getCurrentWindowWidth(); + if (width > 0) { + // Register current window width. + mWidth[orientation] = width; + mIsRegistered[orientation] = true; + } + } + } catch (IndexOutOfBoundsException e) { + Log.w(TAG, "unknwon orientation value " + orientation); + } + } + } + + public class KeyboardLayoutState { + private boolean mIsValid; + private boolean mIsAlphabetMode; + private boolean mIsShiftLocked; + private boolean mIsShifted; + + public boolean isValid() { + return mIsValid; + } + + public void save() { + if (mCurrentId == null) { + return; + } + mIsAlphabetMode = isAlphabetMode(); + if (mIsAlphabetMode) { + mIsShiftLocked = isShiftLocked(); + mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked(); + } else { + mIsShiftLocked = false; + mIsShifted = mCurrentId.equals(mSymbolsShiftedKeyboardId); + } + mIsValid = true; + } + + public KeyboardId getKeyboardId() { + if (!mIsValid) return mMainKeyboardId; + + if (mIsAlphabetMode) { + return mMainKeyboardId; + } else { + return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId; + } + } + + public void restore() { + if (!mIsValid) return; + mIsValid = false; + + if (mIsAlphabetMode) { + final boolean isAlphabetMode = isAlphabetMode(); + final boolean isShiftLocked = isAlphabetMode && isShiftLocked(); + final boolean isShifted = !isShiftLocked && isShiftedOrShiftLocked(); + if (mIsShiftLocked != isShiftLocked) { + toggleCapsLock(); + } else if (mIsShifted != isShifted) { + onPressShift(false); + onReleaseShift(false); + } + } + } + } + public static KeyboardSwitcher getInstance() { return sInstance; } @@ -114,11 +238,18 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public static void init(LatinIME ims, SharedPreferences prefs) { - sInstance.mInputMethodService = ims; - sInstance.mPrefs = prefs; - sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance(); - sInstance.setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs)); - prefs.registerOnSharedPreferenceChangeListener(sInstance); + sInstance.initInternal(ims, prefs); + } + + private void initInternal(LatinIME ims, SharedPreferences prefs) { + mInputMethodService = ims; + mPackageName = ims.getPackageName(); + mResources = ims.getResources(); + mPrefs = prefs; + mSubtypeSwitcher = SubtypeSwitcher.getInstance(); + mWindowWidthCache = new WindowWidthCache(ims); + setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs)); + prefs.registerOnSharedPreferenceChangeListener(this); } private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) { @@ -143,83 +274,105 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - public void loadKeyboard(EditorInfo attribute, Settings.Values settings) { - mSwitchState = SWITCH_STATE_ALPHA; + public void loadKeyboard(EditorInfo editorInfo, Settings.Values settingsValues) { try { - final boolean voiceKeyEnabled = settings.isVoiceKeyEnabled(attribute); - final boolean voiceKeyOnMain = settings.isVoiceKeyOnMain(); - mMainKeyboardId = getKeyboardId( - attribute, false, false, voiceKeyEnabled, voiceKeyOnMain); - mSymbolsKeyboardId = getKeyboardId( - attribute, true, false, voiceKeyEnabled, voiceKeyOnMain); - mSymbolsShiftedKeyboardId = getKeyboardId( - attribute, true, true, voiceKeyEnabled, voiceKeyOnMain); - setKeyboard(getKeyboard(mMainKeyboardId)); + mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues); + mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues); + mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues); + setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId())); } catch (RuntimeException e) { Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e); LatinImeLogger.logOnException(mMainKeyboardId.toString(), e); } } + public KeyboardLayoutState getKeyboardState() { + return mSavedKeyboardState; + } + + public void onFinishInputView() { + mIsAutoCorrectionActive = false; + } + + public void onHideWindow() { + mIsAutoCorrectionActive = false; + } + + public void registerWindowWidth() { + mWindowWidthCache.registerWidth(); + } + @SuppressWarnings("unused") public void onSizeChanged(int w, int h, int oldw, int oldh) { - final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth(); + // TODO: This hack should be removed when display metric returns a proper width. + // Until then, the behavior of KeyboardSwitcher is suboptimal on a device that has a + // vertical system navigation bar in landscape screen orientation, for instance. + final Configuration conf = mResources.getConfiguration(); + final int width = mWindowWidthCache.getWidthOnSizeChanged(conf); // If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with. if (width == 0 || mCurrentId == null) return; - // The window width is fixed. - mWindowWidth = width; - // If this is the first time the {@link KeyboardView} has been shown, no need to reload - // keyboard. - if (oldw == 0 && oldh == 0) - return; // Reload keyboard with new width. - final int orientation = mInputMethodService.getResources().getConfiguration().orientation; - final KeyboardId newId = mCurrentId.cloneWithNewGeometry(orientation, width); - // If the new keyboard is the same as the current one, no need to reload it. - if (newId.equals(mCurrentId)) - return; + final KeyboardId newId = mCurrentId.cloneWithNewGeometry(conf.orientation, width); + mInputMethodService.mHandler.postRestoreKeyboardLayout(); setKeyboard(getKeyboard(newId)); } - private void setKeyboard(final Keyboard newKeyboard) { + private void setKeyboard(final Keyboard keyboard) { final Keyboard oldKeyboard = mKeyboardView.getKeyboard(); - mKeyboardView.setKeyboard(newKeyboard); - mCurrentId = newKeyboard.mId; - final Resources res = mInputMethodService.getResources(); + mKeyboardView.setKeyboard(keyboard); + mCurrentId = keyboard.mId; + mSwitchState = getSwitchState(mCurrentId); + updateShiftLockState(keyboard); mKeyboardView.setKeyPreviewPopupEnabled( - Settings.Values.isKeyPreviewPopupEnabled(mPrefs, res), - Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, res)); + Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources), + Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, mResources)); final boolean localeChanged = (oldKeyboard == null) - || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); + || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); + updateShiftState(); + } + + private int getSwitchState(KeyboardId id) { + return id.equals(mMainKeyboardId) ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN; + } + + private void updateShiftLockState(Keyboard keyboard) { + if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) { + // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a. + // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked() + // that takes care of the current keyboard having such ALT key or not. + keyboard.setShiftLocked(keyboard.hasShiftLockKey()); + } else if (mCurrentId.equals(mSymbolsKeyboardId)) { + // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the + // indicator, we need to call setShiftLocked(false). + keyboard.setShiftLocked(false); + } } private LatinKeyboard getKeyboard(KeyboardId id) { final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id); LatinKeyboard keyboard = (ref == null) ? null : ref.get(); if (keyboard == null) { - final Resources res = mInputMethodService.getResources(); - final Locale savedLocale = Utils.setSystemLocale(res, - mSubtypeSwitcher.getInputLocale()); - - keyboard = new LatinKeyboard(mThemeContext, id, id.mWidth); - - if (id.mEnableShiftLock) { - keyboard.enableShiftLock(); + final Locale savedLocale = Utils.setSystemLocale( + mResources, mSubtypeSwitcher.getInputLocale()); + try { + keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build(); + } finally { + Utils.setSystemLocale(mResources, savedLocale); } - mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard)); - if (DEBUG_CACHE) + + if (DEBUG_CACHE) { Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " + ((ref == null) ? "LOAD" : "GCed") + " id=" + id); - - Utils.setSystemLocale(res, savedLocale); + } } else if (DEBUG_CACHE) { Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id); } keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive); + keyboard.setShiftLocked(false); keyboard.setShifted(false); // If the cached keyboard had been switched to another keyboard while the language was // displayed on its spacebar, it might have had arbitrary text fade factor. In such case, @@ -229,28 +382,16 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return keyboard; } - private static boolean hasSettingsKey(SharedPreferences prefs, Context context, - EditorInfo attribute) { - return getSettingsKeyMode(prefs, context) - && !Utils.inPrivateImeOptions(context.getPackageName(), - LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute); - } - - private KeyboardId getKeyboardId(EditorInfo attribute, final boolean isSymbols, - final boolean isShift, final boolean voiceKeyEnabled, final boolean voiceKeyOnMain) { - final int mode = Utils.getKeyboardMode(attribute); - final boolean hasVoiceKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain); + private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols, + final boolean isShift, Settings.Values settingsValues) { + final int mode = Utils.getKeyboardMode(editorInfo); final int xmlId; - final boolean enableShiftLock; - switch (mode) { case KeyboardId.MODE_PHONE: xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone; - enableShiftLock = true; break; case KeyboardId.MODE_NUMBER: xmlId = R.xml.kbd_number; - enableShiftLock = false; break; default: if (isSymbols) { @@ -258,24 +399,28 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } else { xmlId = R.xml.kbd_qwerty; } - enableShiftLock = true; break; } - final boolean hasSettingsKey = hasSettingsKey(mPrefs, mInputMethodService, attribute); - final int f2KeyMode = getF2KeyMode(mPrefs, mInputMethodService, attribute); - final boolean clobberSettingsKey = Utils.inPrivateImeOptions( - mInputMethodService.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY, - attribute); - final Resources res = mInputMethodService.getResources(); - final int orientation = res.getConfiguration().orientation; - if (mWindowWidth == 0) - mWindowWidth = res.getDisplayMetrics().widthPixels; - final Locale locale = mSubtypeSwitcher.getInputLocale(); + final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled(editorInfo); + final boolean noMicrophone = Utils.inPrivateImeOptions( + mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo) + || Utils.inPrivateImeOptions( + null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo); + final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) + && !noMicrophone; + final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); + final boolean noSettingsKey = Utils.inPrivateImeOptions( + mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo); + final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey; + final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey); + final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain); + final Configuration conf = mResources.getConfiguration(); + return new KeyboardId( - res.getResourceEntryName(xmlId), xmlId, locale, orientation, mWindowWidth, - mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, voiceKeyEnabled, - hasVoiceKey, enableShiftLock); + mResources.getResourceEntryName(xmlId), xmlId, mSubtypeSwitcher.getInputLocale(), + conf.orientation, mWindowWidthCache.getWidth(conf), mode, editorInfo, + hasSettingsKey, f2KeyMode, noSettingsKey, voiceKeyEnabled, hasShortcutKey); } public int getKeyboardMode() { @@ -398,11 +543,12 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } private void setAutomaticTemporaryUpperCase() { - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard != null) { - latinKeyboard.setAutomaticTemporaryUpperCase(); - mKeyboardView.invalidateAllKeys(); + if (mKeyboardView == null) return; + final Keyboard keyboard = mKeyboardView.getKeyboard(); + if (keyboard != null) { + keyboard.setAutomaticTemporaryUpperCase(); } + mKeyboardView.invalidateAllKeys(); } /** @@ -414,7 +560,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha Log.d(TAG, "updateShiftState:" + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState() + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() - + " shiftKeyState=" + shiftKeyState); + + " shiftKeyState=" + shiftKeyState + + " isAlphabetMode=" + isAlphabetMode() + + " isShiftLocked=" + isShiftLocked()); if (isAlphabetMode()) { if (!isShiftLocked() && !shiftKeyState.isIgnoring()) { if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) { @@ -568,27 +716,12 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (mCurrentId.equals(mSymbolsKeyboardId) || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) { keyboard = getKeyboard(mSymbolsShiftedKeyboardId); - // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a. - // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked() - // that takes care of the current keyboard having such ALT key or not. - keyboard.setShiftLocked(hasStickyShiftKey(keyboard)); } else { keyboard = getKeyboard(mSymbolsKeyboardId); - // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the - // indicator, we need to call setShiftLocked(false). - keyboard.setShiftLocked(false); } setKeyboard(keyboard); } - private static boolean hasStickyShiftKey(Keyboard keyboard) { - for (final Key shiftKey : keyboard.getShiftKeys()) { - if (shiftKey.mSticky) - return true; - } - return false; - } - public boolean isInMomentarySwitchState() { return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; @@ -605,10 +738,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private void toggleKeyboardMode() { if (mCurrentId.equals(mMainKeyboardId)) { setKeyboard(getKeyboard(mSymbolsKeyboardId)); - mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } else { setKeyboard(getKeyboard(mMainKeyboardId)); - mSwitchState = SWITCH_STATE_ALPHA; } } @@ -771,9 +902,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (PREF_KEYBOARD_LAYOUT.equals(key)) { - final int layoutId = getKeyboardThemeIndex(mInputMethodService, sharedPreferences); - postSetInputView(createInputView(layoutId, false)); - } else if (Settings.PREF_SETTINGS_KEY.equals(key)) { + final int themeIndex = getKeyboardThemeIndex(mInputMethodService, sharedPreferences); + postSetInputView(createInputView(themeIndex, false)); + } else if (Settings.PREF_SHOW_SETTINGS_KEY.equals(key)) { postSetInputView(createInputView(mThemeIndex, true)); } } @@ -791,41 +922,18 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) { - final Resources res = context.getResources(); - final boolean showSettingsKeyOption = res.getBoolean( - R.bool.config_enable_show_settings_key_option); - if (showSettingsKeyOption) { - final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY, - res.getString(DEFAULT_SETTINGS_KEY_MODE)); - // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or - // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system - if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW)) - || (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_AUTO)) - && Utils.hasMultipleEnabledIMEsOrSubtypes( - (InputMethodManagerCompatWrapper.getInstance())))) { - return true; - } - return false; - } - // If the show settings key option is disabled, we always try showing the settings key. - return true; - } - - private static int getF2KeyMode(SharedPreferences prefs, Context context, - EditorInfo attribute) { - final boolean clobberSettingsKey = Utils.inPrivateImeOptions( - context.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute); - final Resources res = context.getResources(); - final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY, - res.getString(DEFAULT_SETTINGS_KEY_MODE)); - if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_AUTO))) { - return clobberSettingsKey ? KeyboardId.F2KEY_MODE_SHORTCUT_IME - : KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS; - } else if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))) { - return clobberSettingsKey ? KeyboardId.F2KEY_MODE_NONE : KeyboardId.F2KEY_MODE_SETTINGS; - } else { // SETTINGS_KEY_MODE_ALWAYS_HIDE + private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) { + if (noSettingsKey) { + // Never shows the Settings key return KeyboardId.F2KEY_MODE_SHORTCUT_IME; } + + if (settingsKeyEnabled) { + return KeyboardId.F2KEY_MODE_SETTINGS; + } else { + // It should be alright to fall back to the Settings key on 7-inch layouts + // even when the Settings key is not explicitly enabled. + return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS; + } } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index fc47713b8..bc021a690 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -35,6 +35,7 @@ import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.RelativeLayout; import android.widget.TextView; import com.android.inputmethod.compat.FrameLayoutCompatUtils; @@ -82,6 +83,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // HORIZONTAL ELLIPSIS "...", character for popup hint. private static final String POPUP_HINT_CHAR = "\u2026"; + // Margin between the label and the icon on a key that has both of them. + // Specified by the fraction of the key width. + // TODO: Use resource parameter for this value. + private static final float LABEL_ICON_MARGIN = 0.05f; + // Main keyboard private Keyboard mKeyboard; private final KeyDrawParams mKeyDrawParams; @@ -349,15 +355,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { * @param keyboard the keyboard to display in this view */ public void setKeyboard(Keyboard keyboard) { - // Remove any pending messages, except dismissing preview + // Remove any pending dismissing preview mDrawingHandler.cancelAllShowKeyPreviews(); + if (mKeyboard != null) { + PointerTracker.dismissAllKeyPreviews(); + } mKeyboard = keyboard; LatinImeLogger.onSetKeyboard(keyboard); requestLayout(); mDirtyRect.set(0, 0, getWidth(), getHeight()); mBufferNeedsUpdate = true; invalidateAllKeys(); - final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap(); + final int keyHeight = keyboard.mDefaultRowHeight - keyboard.mVerticalGap; mKeyDrawParams.updateKeyHeight(keyHeight); mKeyPreviewDrawParams.updateKeyHeight(keyHeight); } @@ -396,7 +405,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mKeyboard != null) { // The main keyboard expands to the display width. - final int height = mKeyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom(); + final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(widthMeasureSpec, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -443,15 +452,16 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { + getPaddingLeft(); final int keyDrawY = mInvalidatedKey.mY + getPaddingTop(); canvas.translate(keyDrawX, keyDrawY); - onBufferDrawKey(mInvalidatedKey, canvas, mPaint, params, isManualTemporaryUpperCase); + onBufferDrawKey(mInvalidatedKey, mKeyboard, canvas, mPaint, params, + isManualTemporaryUpperCase); canvas.translate(-keyDrawX, -keyDrawY); } else { // Draw all keys. - for (final Key key : mKeyboard.getKeys()) { + for (final Key key : mKeyboard.mKeys) { final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft(); final int keyDrawY = key.mY + getPaddingTop(); canvas.translate(keyDrawX, keyDrawY); - onBufferDrawKey(key, canvas, mPaint, params, isManualTemporaryUpperCase); + onBufferDrawKey(key, mKeyboard, canvas, mPaint, params, isManualTemporaryUpperCase); canvas.translate(-keyDrawX, -keyDrawY); } } @@ -470,8 +480,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { return false; } - private static void onBufferDrawKey(final Key key, final Canvas canvas, Paint paint, - KeyDrawParams params, boolean isManualTemporaryUpperCase) { + private static void onBufferDrawKey(final Key key, final Keyboard keyboard, final Canvas canvas, + Paint paint, KeyDrawParams params, boolean isManualTemporaryUpperCase) { final boolean debugShowAlign = LatinImeLogger.sVISUALDEBUG; // Draw key background. final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight @@ -504,10 +514,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } // Draw key label. + final Drawable icon = key.getIcon(); float positionX = centerX; if (key.mLabel != null) { // Switch the character to uppercase if shift is pressed - final CharSequence label = key.getCaseAdjustedLabel(); + final CharSequence label = keyboard.adjustLabelCase(key.mLabel); // For characters, use large font. For labels like "Done", use smaller font. paint.setTypeface(key.selectTypeface(params.mKeyTextStyle)); final int labelSize = key.selectTextSize(params.mKeyLetterSize, @@ -520,16 +531,27 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final float baseline = centerY + labelCharHeight / 2; // Horizontal label text alignment - if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) { + float labelWidth = 0; + if (key.isAlignLeft()) { positionX = (int)params.mKeyLabelHorizontalPadding; paint.setTextAlign(Align.LEFT); - } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) { + } else if (key.isAlignRight()) { positionX = keyWidth - (int)params.mKeyLabelHorizontalPadding; paint.setTextAlign(Align.RIGHT); - } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0) { + } else if (key.isAlignLeftOfCenter()) { // TODO: Parameterise this? positionX = centerX - labelCharWidth * 7 / 4; paint.setTextAlign(Align.LEFT); + } else if (key.hasLabelWithIconLeft() && icon != null) { + labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth() + + (int)(LABEL_ICON_MARGIN * keyWidth); + positionX = centerX + labelWidth / 2; + paint.setTextAlign(Align.RIGHT); + } else if (key.hasLabelWithIconRight() && icon != null) { + labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth() + + (int)(LABEL_ICON_MARGIN * keyWidth); + positionX = centerX - labelWidth / 2; + paint.setTextAlign(Align.LEFT); } else { positionX = centerX; paint.setTextAlign(Align.CENTER); @@ -551,6 +573,19 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // Turn off drop shadow paint.setShadowLayer(0, 0, 0, 0); + if (icon != null) { + final int iconWidth = icon.getIntrinsicWidth(); + final int iconHeight = icon.getIntrinsicHeight(); + final int iconY = (keyHeight - iconHeight) / 2; + if (key.hasLabelWithIconLeft()) { + final int iconX = (int)(centerX - labelWidth / 2); + drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); + } else if (key.hasLabelWithIconRight()) { + final int iconX = (int)(centerX + labelWidth / 2 - iconWidth); + drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); + } + } + if (debugShowAlign) { final Paint line = new Paint(); drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line); @@ -604,16 +639,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } // Draw key icon. - final Drawable icon = key.getIcon(); if (key.mLabel == null && icon != null) { final int iconWidth = icon.getIntrinsicWidth(); final int iconHeight = icon.getIntrinsicHeight(); final int iconX, alignX; final int iconY = (keyHeight - iconHeight) / 2; - if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) { + if (key.isAlignLeft()) { iconX = (int)params.mKeyLabelHorizontalPadding; alignX = iconX; - } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) { + } else if (key.isAlignRight()) { iconX = keyWidth - (int)params.mKeyLabelHorizontalPadding - iconWidth; alignX = iconX + iconWidth; } else { // Align center @@ -630,7 +664,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } // Draw popup hint "..." at the bottom right corner of the key. - if (key.hasPopupHint()) { + if ((key.hasPopupHint() && key.mPopupCharacters != null && key.mPopupCharacters.length > 0) + || key.needsSpecialPopupHint()) { paint.setTextSize(params.mKeyHintLetterSize); paint.setColor(params.mKeyHintLabelColor); paint.setTextAlign(Align.CENTER); @@ -693,6 +728,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { return width; } + private static float getLabelWidth(CharSequence label, Paint paint) { + paint.getTextBounds(label.toString(), 0, label.length(), sTextBounds); + return sTextBounds.width(); + } + private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width, int height) { canvas.translate(x, y); @@ -701,7 +741,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { canvas.translate(-x, -y); } - private static void drawHorizontalLine(Canvas canvas, float y, float w, int color, Paint paint) { + private static void drawHorizontalLine(Canvas canvas, float y, float w, int color, + Paint paint) { paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(1.0f); paint.setColor(color); @@ -754,22 +795,21 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { @Override public void dismissKeyPreview(PointerTracker tracker) { - if (mShowKeyPreviewPopup) { - mDrawingHandler.cancelShowKeyPreview(tracker); - mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker); - } + mDrawingHandler.cancelShowKeyPreview(tracker); + mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker); } private void addKeyPreview(TextView keyPreview) { if (mPreviewPlacer == null) { - mPreviewPlacer = FrameLayoutCompatUtils.getPlacer( - (ViewGroup)getRootView().findViewById(android.R.id.content)); + mPreviewPlacer = new RelativeLayout(getContext()); + final ViewGroup windowContentView = + (ViewGroup)getRootView().findViewById(android.R.id.content); + windowContentView.addView(mPreviewPlacer); } - final ViewGroup placer = mPreviewPlacer; - placer.addView(keyPreview, FrameLayoutCompatUtils.newLayoutParam(placer, 0, 0)); + mPreviewPlacer.addView( + keyPreview, FrameLayoutCompatUtils.newLayoutParam(mPreviewPlacer, 0, 0)); } - // TODO: Introduce minimum duration for displaying key previews private void showKey(final int keyIndex, PointerTracker tracker) { final TextView previewText = tracker.getKeyPreviewText(); // If the key preview has no parent view yet, add it to the ViewGroup which can place @@ -800,7 +840,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize); previewText.setTypeface(params.mKeyTextStyle); } - previewText.setText(key.getCaseAdjustedLabel()); + previewText.setText(mKeyboard.adjustLabelCase(key.mLabel)); } else { final Drawable previewIcon = key.getPreviewIcon(); previewText.setCompoundDrawables(null, null, null, @@ -888,5 +928,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public void onDetachedFromWindow() { super.onDetachedFromWindow(); closing(); + if (mPreviewPlacer != null) { + mPreviewPlacer.removeAllViews(); + } } } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index 3c27129ec..1b6f57b92 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -31,13 +31,16 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.keyboard.internal.KeyboardBuilder; +import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; +import com.android.inputmethod.latin.Utils; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Locale; // TODO: We should remove this class @@ -58,6 +61,7 @@ public class LatinKeyboard extends Keyboard { private float mSpacebarTextFadeFactor = 0.0f; private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache = new HashMap<Integer, SoftReference<BitmapDrawable>>(); + private final boolean mIsSpacebarTriggeringPopupByLongPress; /* Shortcut key and its icons if available */ private final Key mShortcutKey; @@ -73,33 +77,20 @@ public class LatinKeyboard extends Keyboard { private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small"; private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium"; - public LatinKeyboard(Context context, KeyboardId id, int width) { - super(context, id.getXmlId(), id, width); + private LatinKeyboard(Context context, LatinKeyboardParams params) { + super(params); mRes = context.getResources(); mTheme = context.getTheme(); - final List<Key> keys = getKeys(); - int spaceKeyIndex = -1; - int shortcutKeyIndex = -1; - final int keyCount = keys.size(); - for (int index = 0; index < keyCount; index++) { - // For now, assuming there are up to one space key and one shortcut key respectively. - switch (keys.get(index).mCode) { - case CODE_SPACE: - spaceKeyIndex = index; - break; - case CODE_SHORTCUT: - shortcutKeyIndex = index; - break; - } - } - // The index of space key is available only after Keyboard constructor has finished. - mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null; + mSpaceKey = params.mSpaceKey; mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null; - mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null; + mShortcutKey = params.mShortcutKey; mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null; + final int longPressSpaceKeyTimeout = + mRes.getInteger(R.integer.config_long_press_space_key_timeout); + mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0); final TypedArray a = context.obtainStyledAttributes( null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard); @@ -114,6 +105,42 @@ public class LatinKeyboard extends Keyboard { a.recycle(); } + private static class LatinKeyboardParams extends KeyboardParams { + public Key mSpaceKey = null; + public Key mShortcutKey = null; + + @Override + public void onAddKey(Key key) { + super.onAddKey(key); + + switch (key.mCode) { + case Keyboard.CODE_SPACE: + mSpaceKey = key; + break; + case Keyboard.CODE_SHORTCUT: + mShortcutKey = key; + break; + } + } + } + + public static class Builder extends KeyboardBuilder<LatinKeyboardParams> { + public Builder(Context context) { + super(context, new LatinKeyboardParams()); + } + + @Override + public Builder load(KeyboardId id) { + super.load(id); + return this; + } + + @Override + public LatinKeyboard build() { + return new LatinKeyboard(mContext, mParams); + } + } + public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) { mSpacebarTextFadeFactor = fadeFactor; updateSpacebarForLocale(false); @@ -158,8 +185,13 @@ public class LatinKeyboard extends Keyboard { } private void updateSpacebarForLocale(boolean isAutoCorrection) { - if (mSpaceKey == null) - return; + if (mSpaceKey == null) return; + final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance(); + if (imm == null) return; + // The "..." popup hint for triggering something by a long-pressing the spacebar + final boolean shouldShowInputMethodPicker = mIsSpacebarTriggeringPopupByLongPress + && Utils.hasMultipleEnabledIMEsOrSubtypes(imm, true /* include aux subtypes */); + mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker); // If application locales are explicitly selected. if (mSubtypeSwitcher.needsToDisplayLanguage()) { mSpaceKey.setIcon(getSpaceDrawable( @@ -294,8 +326,8 @@ public class LatinKeyboard extends Keyboard { @Override public int[] getNearestKeys(int x, int y) { // Avoid dead pixels at edges of the keyboard - return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)), - Math.max(0, Math.min(y, getHeight() - 1))); + return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)), + Math.max(0, Math.min(y, mOccupiedHeight - 1))); } public static int getTextSizeFromTheme(Theme theme, int style, int defValue) { diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java index cb1a2b782..12aadcb5c 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java @@ -281,15 +281,12 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke */ @Override public void setKeyboard(Keyboard keyboard) { - if (getKeyboard() != null) { - PointerTracker.dismissAllKeyPreviews(); - } // Remove any pending messages, except dismissing preview mKeyTimerHandler.cancelKeyTimers(); super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); - mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth()); + mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth); PointerTracker.setKeyDetector(mKeyDetector); mPopupPanelCache.clear(); } @@ -360,7 +357,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view); final Keyboard parentKeyboard = getKeyboard(); final Keyboard miniKeyboard = new MiniKeyboardBuilder( - this, parentKeyboard.getPopupKeyboardResId(), parentKey, parentKeyboard).build(); + this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build(); miniKeyboardView.setKeyboard(miniKeyboard); container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), @@ -567,9 +564,9 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke @Override public boolean dispatchTouchEvent(MotionEvent event) { + // Drop non-hover touch events when touch exploration is enabled. if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event) - || super.dispatchTouchEvent(event); + return false; } return super.dispatchTouchEvent(event); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index c404a5dfb..dad37e728 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -23,6 +23,7 @@ import android.util.Log; import android.view.MotionEvent; import com.android.inputmethod.deprecated.VoiceProxy; +import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.Utils; @@ -53,55 +54,60 @@ public class LatinKeyboardView extends LatinKeyboardBaseView { @Override public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard != null - && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) { - // Phone and number keyboard never shows popup preview (except language switch). - super.setKeyPreviewPopupEnabled(false, delay); - } else { - super.setKeyPreviewPopupEnabled(previewEnabled, delay); + final Keyboard keyboard = getKeyboard(); + if (keyboard instanceof LatinKeyboard) { + final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard; + if (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard()) { + // Phone and number keyboard never shows popup preview. + super.setKeyPreviewPopupEnabled(false, delay); + return; + } } + super.setKeyPreviewPopupEnabled(previewEnabled, delay); } @Override public void setKeyboard(Keyboard newKeyboard) { super.setKeyboard(newKeyboard); // One-seventh of the keyboard width seems like a reasonable threshold - mJumpThresholdSquare = newKeyboard.getMinWidth() / 7; - mJumpThresholdSquare *= mJumpThresholdSquare; - } - - private LatinKeyboard getLatinKeyboard() { - Keyboard keyboard = getKeyboard(); - if (keyboard instanceof LatinKeyboard) { - return (LatinKeyboard)keyboard; - } else { - return null; - } + final int jumpThreshold = newKeyboard.mOccupiedWidth / 7; + mJumpThresholdSquare = jumpThreshold * jumpThreshold; } public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) { - final LatinKeyboard currentKeyboard = getLatinKeyboard(); + final Keyboard keyboard = getKeyboard(); // We should not set text fade factor to the keyboard which does not display the language on // its spacebar. - if (currentKeyboard != null && currentKeyboard == oldKeyboard) - currentKeyboard.setSpacebarTextFadeFactor(fadeFactor, this); + if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) { + ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this); + } } @Override protected boolean onLongPress(Key key, PointerTracker tracker) { - int primaryCode = key.mCode; + final int primaryCode = key.mCode; + final Keyboard keyboard = getKeyboard(); + if (keyboard instanceof LatinKeyboard) { + final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard; + if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) { + tracker.onLongPressed(); + // Long pressing on 0 in phone number keypad gives you a '+'. + return invokeOnKey(Keyboard.CODE_PLUS); + } + if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) { + tracker.onLongPressed(); + return invokeOnKey(Keyboard.CODE_CAPSLOCK); + } + } if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) { - tracker.onLongPressed(); // Both long pressing settings key and space key invoke IME switcher dialog. - return invokeOnKey(Keyboard.CODE_SETTINGS_LONGPRESS); - } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) { - tracker.onLongPressed(); - // Long pressing on 0 in phone number keypad gives you a '+'. - return invokeOnKey('+'); - } else if (primaryCode == Keyboard.CODE_SHIFT) { - tracker.onLongPressed(); - return invokeOnKey(Keyboard.CODE_CAPSLOCK); + if (getKeyboardActionListener().onCustomRequest( + LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { + tracker.onLongPressed(); + return true; + } else { + return super.onLongPress(key, tracker); + } } else { return super.onLongPress(key, tracker); } @@ -194,7 +200,7 @@ public class LatinKeyboardView extends LatinKeyboardBaseView { @Override public boolean onTouchEvent(MotionEvent me) { - if (getLatinKeyboard() == null) return true; + if (getKeyboard() == null) return true; // If there was a sudden jump, return without processing the actual motion event. if (handleSuddenJump(me)) { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index 95e32755e..08e7d7e19 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -16,30 +16,14 @@ package com.android.inputmethod.keyboard; -import android.content.Context; +import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardParams; public class MiniKeyboard extends Keyboard { - private int mDefaultKeyCoordX; + private final int mDefaultKeyCoordX; - public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) { - super(context, xmlLayoutResId, parentKeyboard.mId.cloneAsMiniKeyboard(), - parentKeyboard.getMinWidth()); - // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal - // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are - // needed to keep having the same horizontal and vertical key spacing. - setHorizontalGap(0); - setVerticalGap(parentKeyboard.getVerticalGap() / 2); - - // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard, - // revert the above hacks and uncomment the following lines. - //setHorizontalGap(parentKeyboard.getHorizontalGap()); - //setVerticalGap(parentKeyboard.getVerticalGap()); - - setRtlKeyboard(parentKeyboard.isRtlKeyboard()); - } - - public void setDefaultCoordX(int pos) { - mDefaultKeyCoordX = pos; + public MiniKeyboard(MiniKeyboardParams params) { + super(params); + mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2; } public int getDefaultCoordX() { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java index 1ec0dda15..84bd44c30 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java @@ -37,7 +37,7 @@ public class MiniKeyboardKeyDetector extends KeyDetector { @Override public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) { - final List<Key> keys = getKeyboard().getKeys(); + final List<Key> keys = getKeyboard().mKeys; final int touchX = getTouchX(x); final int touchY = getTouchY(y); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 6b6a4538f..1f8119a0f 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -151,6 +151,8 @@ public class PointerTracker { public void onTextInput(CharSequence text) {} @Override public void onCancelInput() {} + @Override + public boolean onCustomRequest(int requestCode) { return false; } }; public static void init(boolean hasDistinctMultitouch, Context context) { @@ -285,8 +287,8 @@ public class PointerTracker { public void setKeyDetectorInner(KeyDetector keyDetector) { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); - mKeys = mKeyboard.getKeys(); - final int keyQuarterWidth = mKeyboard.getKeyWidth() / 4; + mKeys = mKeyboard.mKeys; + final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4; mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; } @@ -329,36 +331,28 @@ public class PointerTracker { return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); } - public boolean isSpaceKey(int keyIndex) { - Key key = getKey(keyIndex); - return key != null && key.mCode == Keyboard.CODE_SPACE; - } - private void setReleasedKeyGraphics(int keyIndex) { mDrawingProxy.dismissKeyPreview(this); final Key key = getKey(keyIndex); - if (key != null) { + if (key != null && key.isEnabled()) { key.onReleased(); mDrawingProxy.invalidateKey(key); } } private void setPressedKeyGraphics(int keyIndex) { - if (isKeyPreviewRequired(keyIndex)) { - mDrawingProxy.showKeyPreview(keyIndex, this); - } final Key key = getKey(keyIndex); if (key != null && key.isEnabled()) { + if (isKeyPreviewRequired(key)) { + mDrawingProxy.showKeyPreview(keyIndex, this); + } key.onPressed(); mDrawingProxy.invalidateKey(key); } } // The modifier key, such as shift key, should not show its key preview. - private boolean isKeyPreviewRequired(int keyIndex) { - final Key key = getKey(keyIndex); - if (key == null || !key.isEnabled()) - return false; + private static boolean isKeyPreviewRequired(Key key) { final int code = key.mCode; if (isModifierCode(code) || code == Keyboard.CODE_DELETE || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE) diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java index 2741ee80b..fb932e3e8 100644 --- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java @@ -79,6 +79,8 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel { public void onRelease(int primaryCode, boolean withSliding) { mParentKeyboardView.getKeyboardActionListener().onRelease(primaryCode, withSliding); } + @Override + public boolean onCustomRequest(int requestCode) { return false; } }; public PopupMiniKeyboardView(Context context, AttributeSet attrs) { @@ -108,8 +110,8 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final Keyboard keyboard = getKeyboard(); if (keyboard != null) { - final int width = keyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); - final int height = keyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom(); + final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); + final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -170,9 +172,9 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel { final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX() + parentKeyboardView.getPaddingLeft(); final int x = Math.max(0, Math.min(miniKeyboardLeft, - parentKeyboardView.getWidth() - miniKeyboard.getMinWidth())) + parentKeyboardView.getWidth() - miniKeyboard.mOccupiedWidth)) - container.getPaddingLeft() + mCoordinates[0]; - final int y = pointY - parentKeyboard.getVerticalGap() + final int y = pointY - parentKeyboard.mVerticalGap - (container.getMeasuredHeight() - container.getPaddingBottom()) + parentKeyboardView.getPaddingTop() + mCoordinates[1]; diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index aadedc69d..7190b051d 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -17,8 +17,10 @@ package com.android.inputmethod.keyboard; import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class ProximityInfo { @@ -54,6 +56,19 @@ public class ProximityInfo { computeNearestNeighbors(keyWidth, keys); } + public static ProximityInfo createDummyProximityInfo() { + return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList()); + } + + public static ProximityInfo createSpellCheckerProximityInfo() { + final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo(); + spellCheckerProximityInfo.mNativeProximityInfo = + spellCheckerProximityInfo.setProximityInfoNative( + SpellCheckerProximityInfo.ROW_SIZE, + 480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY); + return spellCheckerProximityInfo; + } + private int mNativeProximityInfo; static { Utils.loadNativeLibrary(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 30d9692a8..c0dba4173 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -20,7 +20,7 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.util.Log; -import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; +import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException; import com.android.inputmethod.latin.R; import java.util.ArrayList; @@ -212,19 +212,19 @@ public class KeyStyles { public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs, XmlResourceParser parser) { - String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName); + final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName); if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />", - KeyboardParser.TAG_KEY_STYLE, styleName)); + KeyboardBuilder.TAG_KEY_STYLE, styleName)); if (mStyles.containsKey(styleName)) throw new ParseException("duplicate key style declared: " + styleName, parser); final DeclaredKeyStyle style = new DeclaredKeyStyle(); if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) { - String parentStyle = keyStyleAttr.getString( + final String parentStyle = keyStyleAttr.getString( R.styleable.Keyboard_KeyStyle_parentStyle); final DeclaredKeyStyle parent = mStyles.get(parentStyle); if (parent == null) - throw new ParseException("Unknown parentStyle " + parent, parser); + throw new ParseException("Unknown parentStyle " + parentStyle, parser); style.addParent(parent); } style.parseKeyStyleAttributes(keyAttrs); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 8eae2bb42..f599def36 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.util.Xml; @@ -36,12 +37,11 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Arrays; -import java.util.List; /** - * Parser for BaseKeyboard. + * Keyboard Building helper. * - * This class parses Keyboard XML file and fill out keys in Keyboard. + * This class parses Keyboard XML file and eventually build a Keyboard. * The Keyboard XML file looks like: * <pre> * >!-- xml/keyboard.xml --< @@ -107,8 +107,8 @@ import java.util.List; * </pre> */ -public class KeyboardParser { - private static final String TAG = KeyboardParser.class.getSimpleName(); +public class KeyboardBuilder<KP extends KeyboardParams> { + private static final String TAG = KeyboardBuilder.class.getSimpleName(); private static final boolean DEBUG = false; // Keyboard XML Tags @@ -123,40 +123,53 @@ public class KeyboardParser { private static final String TAG_DEFAULT = "default"; public static final String TAG_KEY_STYLE = "key-style"; - private final Keyboard mKeyboard; - private final Context mContext; - private final Resources mResources; + protected final KP mParams; + protected final Context mContext; + protected final Resources mResources; + private final DisplayMetrics mDisplayMetrics; - private int mKeyboardTopPadding; - private int mKeyboardBottomPadding; - private int mHorizontalEdgesPadding; private int mCurrentX = 0; private int mCurrentY = 0; - private int mMaxRowWidth = 0; - private int mTotalHeight = 0; private Row mCurrentRow = null; private boolean mLeftEdge; + private boolean mTopEdge; private Key mRightEdgeKey = null; private final KeyStyles mKeyStyles = new KeyStyles(); - public KeyboardParser(Keyboard keyboard, Context context) { - mKeyboard = keyboard; + public KeyboardBuilder(Context context, KP params) { mContext = context; final Resources res = context.getResources(); mResources = res; - mHorizontalEdgesPadding = (int)res.getDimension(R.dimen.keyboard_horizontal_edges_padding); + mDisplayMetrics = res.getDisplayMetrics(); + + mParams = params; + mParams.mHorizontalEdgesPadding = (int)res.getDimension( + R.dimen.keyboard_horizontal_edges_padding); + + mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); + mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); } - public int getMaxRowWidth() { - return mMaxRowWidth; + public KeyboardBuilder<KP> load(KeyboardId id) { + mParams.mId = id; + try { + parseKeyboard(id.getXmlId()); + } catch (XmlPullParserException e) { + Log.w(TAG, "keyboard XML parse error: " + e); + throw new IllegalArgumentException(e); + } catch (IOException e) { + Log.w(TAG, "keyboard XML parse error: " + e); + throw new RuntimeException(e); + } + return this; } - public int getTotalHeight() { - return mTotalHeight; + public Keyboard build() { + return new Keyboard(mParams); } - public void parseKeyboard(int resId) throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mKeyboard.mId)); + private void parseKeyboard(int resId) throws XmlPullParserException, IOException { + if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId)); final XmlResourceParser parser = mResources.getXml(resId); int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -165,7 +178,7 @@ public class KeyboardParser { if (TAG_KEYBOARD.equals(tag)) { parseKeyboardAttributes(parser); startKeyboard(); - parseKeyboardContent(parser, mKeyboard.getKeys()); + parseKeyboardContent(parser, false); break; } else { throw new IllegalStartTag(parser, TAG_KEYBOARD); @@ -196,15 +209,14 @@ public class KeyboardParser { } private void parseKeyboardAttributes(XmlResourceParser parser) { - final Keyboard keyboard = mKeyboard; - final int displayWidth = keyboard.getDisplayWidth(); + final int displayWidth = mDisplayMetrics.widthPixels; final TypedArray keyboardAttr = mContext.obtainStyledAttributes( Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard); final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); try { - final int displayHeight = keyboard.getDisplayHeight(); + final int displayHeight = mDisplayMetrics.heightPixels; final int keyboardHeight = (int)keyboardAttr.getDimension( R.styleable.Keyboard_keyboardHeight, displayHeight / 2); final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr, @@ -219,61 +231,67 @@ public class KeyboardParser { } // Keyboard height will not exceed maxKeyboardHeight and will not be less than // minKeyboardHeight. - final int height = Math.max( + mParams.mOccupiedHeight = Math.max( Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); - - keyboard.setKeyboardHeight(height); - keyboard.setRtlKeyboard(keyboardAttr.getBoolean( - R.styleable.Keyboard_isRtlKeyboard, false)); - keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyWidth, displayWidth, displayWidth / 10)); - keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_rowHeight, height, 50)); - keyboard.setHorizontalGap(getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_horizontalGap, displayWidth, 0)); - keyboard.setVerticalGap(getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_verticalGap, height, 0)); - keyboard.setPopupKeyboardResId(keyboardAttr.getResourceId( - R.styleable.Keyboard_popupKeyboardTemplate, 0)); - keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt( - R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5)); - - mKeyboard.mIconsSet.loadIcons(keyboardAttr); - mKeyboardTopPadding = getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyboardTopPadding, height, 0); - mKeyboardBottomPadding = getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyboardBottomPadding, height, 0); + mParams.mOccupiedWidth = mParams.mId.mWidth; + mParams.mTopPadding = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyboardTopPadding, mParams.mOccupiedHeight, 0); + mParams.mBottomPadding = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyboardBottomPadding, mParams.mOccupiedHeight, 0); + + final int height = mParams.mOccupiedHeight; + final int width = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding * 2 + - mParams.mHorizontalCenterPadding; + mParams.mHeight = height; + mParams.mWidth = width; + mParams.mDefaultKeyWidth = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyWidth, width, width / 10); + mParams.mDefaultRowHeight = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_rowHeight, height, height / 4); + mParams.mHorizontalGap = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_horizontalGap, width, 0); + mParams.mVerticalGap = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_verticalGap, height, 0); + + mParams.mIsRtlKeyboard = keyboardAttr.getBoolean( + R.styleable.Keyboard_isRtlKeyboard, false); + mParams.mPopupKeyboardResId = keyboardAttr.getResourceId( + R.styleable.Keyboard_popupKeyboardTemplate, 0); + mParams.mMaxPopupColumn = keyAttr.getInt( + R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5); + + mParams.mIconsSet.loadIcons(keyboardAttr); } finally { keyAttr.recycle(); keyboardAttr.recycle(); } } - private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys) + private void parseKeyboardContent(XmlResourceParser parser, boolean skip) throws XmlPullParserException, IOException { int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_ROW.equals(tag)) { - Row row = new Row(mResources, mKeyboard, parser); + Row row = parseRowAttributes(parser); if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW)); - if (keys != null) + if (!skip) startRow(row); - parseRowContent(parser, row, keys); + parseRowContent(parser, row, skip); } else if (TAG_INCLUDE.equals(tag)) { - parseIncludeKeyboardContent(parser, keys); + parseIncludeKeyboardContent(parser, skip); } else if (TAG_SWITCH.equals(tag)) { - parseSwitchKeyboardContent(parser, keys); + parseSwitchKeyboardContent(parser, skip); } else if (TAG_KEY_STYLE.equals(tag)) { - parseKeyStyle(parser, keys); + parseKeyStyle(parser, skip); } else { throw new IllegalStartTag(parser, TAG_ROW); } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); if (TAG_KEYBOARD.equals(tag)) { - endKeyboard(mKeyboard.getVerticalGap()); + endKeyboard(); break; } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) { @@ -288,22 +306,36 @@ public class KeyboardParser { } } - private void parseRowContent(XmlResourceParser parser, Row row, List<Key> keys) + private Row parseRowAttributes(XmlResourceParser parser) { + final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard); + try { + if (a.hasValue(R.styleable.Keyboard_horizontalGap)) + throw new IllegalAttribute(parser, "horizontalGap"); + if (a.hasValue(R.styleable.Keyboard_verticalGap)) + throw new IllegalAttribute(parser, "verticalGap"); + return new Row(mResources, mParams, parser); + } finally { + a.recycle(); + } + } + + private void parseRowContent(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_KEY.equals(tag)) { - parseKey(parser, row, keys); + parseKey(parser, row, skip); } else if (TAG_SPACER.equals(tag)) { - parseSpacer(parser, row, keys); + parseSpacer(parser, row, skip); } else if (TAG_INCLUDE.equals(tag)) { - parseIncludeRowContent(parser, row, keys); + parseIncludeRowContent(parser, row, skip); } else if (TAG_SWITCH.equals(tag)) { - parseSwitchRowContent(parser, row, keys); + parseSwitchRowContent(parser, row, skip); } else if (TAG_KEY_STYLE.equals(tag)) { - parseKeyStyle(parser, keys); + parseKeyStyle(parser, skip); } else { throw new IllegalStartTag(parser, TAG_KEY); } @@ -311,7 +343,7 @@ public class KeyboardParser { final String tag = parser.getName(); if (TAG_ROW.equals(tag)) { if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW)); - if (keys != null) + if (!skip) endRow(); break; } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) @@ -327,26 +359,24 @@ public class KeyboardParser { } } - private void parseKey(XmlResourceParser parser, Row row, List<Key> keys) + private void parseKey(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - if (keys == null) { + if (skip) { checkEndTag(TAG_KEY, parser); } else { - Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles); + Key key = new Key(mResources, mParams, row, mCurrentX, mCurrentY, parser, mKeyStyles); if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />", TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode, Arrays.toString(key.mPopupCharacters))); checkEndTag(TAG_KEY, parser); - keys.add(key); - if (key.mCode == Keyboard.CODE_SHIFT) - mKeyboard.getShiftKeys().add(key); + mParams.onAddKey(key); endKey(key); } } - private void parseSpacer(XmlResourceParser parser, Row row, List<Key> keys) + private void parseSpacer(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - if (keys == null) { + if (skip) { checkEndTag(TAG_SPACER, parser); } else { if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); @@ -354,14 +384,14 @@ public class KeyboardParser { R.styleable.Keyboard); if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap)) throw new IllegalAttribute(parser, "horizontalGap"); - final int keyboardWidth = mKeyboard.getDisplayWidth(); + final int keyboardWidth = mParams.mWidth; final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth, - keyboardWidth, row.mDefaultWidth); + keyboardWidth, row.mDefaultKeyWidth); keyboardAttr.recycle(); final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); - int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, + int keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_keyXPos, keyboardWidth, mCurrentX); if (keyXPos < 0) { // If keyXPos is negative, the actual x-coordinate will be display_width + keyXPos. @@ -373,19 +403,19 @@ public class KeyboardParser { } } - private void parseIncludeKeyboardContent(XmlResourceParser parser, List<Key> keys) + private void parseIncludeKeyboardContent(XmlResourceParser parser, boolean skip) throws XmlPullParserException, IOException { - parseIncludeInternal(parser, null, keys); + parseIncludeInternal(parser, null, skip); } - private void parseIncludeRowContent(XmlResourceParser parser, Row row, List<Key> keys) + private void parseIncludeRowContent(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - parseIncludeInternal(parser, row, keys); + parseIncludeInternal(parser, row, skip); } - private void parseIncludeInternal(XmlResourceParser parser, Row row, List<Key> keys) + private void parseIncludeInternal(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - if (keys == null) { + if (skip) { checkEndTag(TAG_INCLUDE, parser); } else { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), @@ -399,11 +429,11 @@ public class KeyboardParser { throw new ParseException("No keyboardLayout attribute in <include/>", parser); if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); - parseMerge(mResources.getLayout(keyboardLayout), row, keys); + parseMerge(mResources.getLayout(keyboardLayout), row, skip); } } - private void parseMerge(XmlResourceParser parser, Row row, List<Key> keys) + private void parseMerge(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -411,9 +441,9 @@ public class KeyboardParser { final String tag = parser.getName(); if (TAG_MERGE.equals(tag)) { if (row == null) { - parseKeyboardContent(parser, keys); + parseKeyboardContent(parser, skip); } else { - parseRowContent(parser, row, keys); + parseRowContent(parser, row, skip); } break; } else { @@ -424,28 +454,28 @@ public class KeyboardParser { } } - private void parseSwitchKeyboardContent(XmlResourceParser parser, List<Key> keys) + private void parseSwitchKeyboardContent(XmlResourceParser parser, boolean skip) throws XmlPullParserException, IOException { - parseSwitchInternal(parser, null, keys); + parseSwitchInternal(parser, null, skip); } - private void parseSwitchRowContent(XmlResourceParser parser, Row row, List<Key> keys) + private void parseSwitchRowContent(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - parseSwitchInternal(parser, row, keys); + parseSwitchInternal(parser, row, skip); } - private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys) + private void parseSwitchInternal(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mKeyboard.mId)); + if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId)); boolean selected = false; int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_CASE.equals(tag)) { - selected |= parseCase(parser, row, selected ? null : keys); + selected |= parseCase(parser, row, selected ? true : skip); } else if (TAG_DEFAULT.equals(tag)) { - selected |= parseDefault(parser, row, selected ? null : keys); + selected |= parseDefault(parser, row, selected ? true : skip); } else { throw new IllegalStartTag(parser, TAG_KEY); } @@ -461,21 +491,21 @@ public class KeyboardParser { } } - private boolean parseCase(XmlResourceParser parser, Row row, List<Key> keys) + private boolean parseCase(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { final boolean selected = parseCaseCondition(parser); if (row == null) { // Processing Rows. - parseKeyboardContent(parser, selected ? keys : null); + parseKeyboardContent(parser, selected ? skip : true); } else { // Processing Keys. - parseRowContent(parser, row, selected ? keys : null); + parseRowContent(parser, row, selected ? skip : true); } return selected; } private boolean parseCaseCondition(XmlResourceParser parser) { - final KeyboardId id = mKeyboard.mId; + final KeyboardId id = mParams.mId; if (id == null) return true; @@ -496,10 +526,10 @@ public class KeyboardParser { R.styleable.Keyboard_Case_f2KeyMode, id.mF2KeyMode); final boolean clobberSettingsKeyMatched = matchBoolean(a, R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); - final boolean voiceEnabledMatched = matchBoolean(a, - R.styleable.Keyboard_Case_voiceKeyEnabled, id.mVoiceKeyEnabled); - final boolean voiceKeyMatched = matchBoolean(a, - R.styleable.Keyboard_Case_hasVoiceKey, id.mHasVoiceKey); + final boolean shortcutKeyEnabledMatched = matchBoolean(a, + R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled); + final boolean hasShortcutKeyMatched = matchBoolean(a, + R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); // As noted at {@link KeyboardId} class, we are interested only in enum value masked by // {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching @@ -514,7 +544,7 @@ public class KeyboardParser { R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); final boolean selected = modeMatched && navigateActionMatched && passwordInputMatched && hasSettingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched - && voiceEnabledMatched && voiceKeyMatched && imeActionMatched && + && shortcutKeyEnabledMatched && hasShortcutKeyMatched && imeActionMatched && localeCodeMatched && languageCodeMatched && countryCodeMatched; if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, @@ -526,8 +556,9 @@ public class KeyboardParser { a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"), booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey, "clobberSettingsKey"), - booleanAttr(a, R.styleable.Keyboard_Case_voiceKeyEnabled, "voiceKeyEnabled"), - booleanAttr(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"), + booleanAttr( + a, R.styleable.Keyboard_Case_shortcutKeyEnabled, "shortcutKeyEnabled"), + booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), textAttr(EditorInfoCompatUtils.imeOptionsName( a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"), textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"), @@ -583,18 +614,18 @@ public class KeyboardParser { return false; } - private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys) + private boolean parseDefault(XmlResourceParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT)); if (row == null) { - parseKeyboardContent(parser, keys); + parseKeyboardContent(parser, skip); } else { - parseRowContent(parser, row, keys); + parseRowContent(parser, row, skip); } return true; } - private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) { + private void parseKeyStyle(XmlResourceParser parser, boolean skip) { TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_KeyStyle); TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), @@ -603,7 +634,7 @@ public class KeyboardParser { if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) throw new ParseException("<" + TAG_KEY_STYLE + "/> needs styleName attribute", parser); - if (keys != null) + if (!skip) mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); } finally { keyStyleAttr.recycle(); @@ -619,12 +650,13 @@ public class KeyboardParser { } private void startKeyboard() { - mCurrentY += mKeyboardTopPadding; + mCurrentY += mParams.mTopPadding; + mTopEdge = true; } private void startRow(Row row) { mCurrentX = 0; - setSpacer(mCurrentX, mHorizontalEdgesPadding); + setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding); mCurrentRow = row; mLeftEdge = true; mRightEdgeKey = null; @@ -637,25 +669,25 @@ public class KeyboardParser { mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT); mRightEdgeKey = null; } - setSpacer(mCurrentX, mHorizontalEdgesPadding); - if (mCurrentX > mMaxRowWidth) - mMaxRowWidth = mCurrentX; - mCurrentY += mCurrentRow.mDefaultHeight; + setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding); + mCurrentY += mCurrentRow.mRowHeight; mCurrentRow = null; + mTopEdge = false; } private void endKey(Key key) { - mCurrentX = key.mX - key.mGap / 2 + key.mWidth + key.mGap; + mCurrentX = key.mX - key.mHorizontalGap / 2 + key.mWidth + key.mHorizontalGap; if (mLeftEdge) { key.addEdgeFlags(Keyboard.EDGE_LEFT); mLeftEdge = false; } + if (mTopEdge) { + key.addEdgeFlags(Keyboard.EDGE_TOP); + } mRightEdgeKey = key; } - private void endKeyboard(int defaultVerticalGap) { - mCurrentY += mKeyboardBottomPadding; - mTotalHeight = mCurrentY - defaultVerticalGap; + private void endKeyboard() { } private void setSpacer(int keyXPos, int width) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 02c261bcd..2d8b7bf11 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -21,7 +21,6 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.Log; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; public class KeyboardIconsSet { @@ -31,40 +30,33 @@ public class KeyboardIconsSet { // This should be aligned with Keyboard.keyIcon enum. private static final int ICON_SHIFT_KEY = 1; - private static final int ICON_TO_SYMBOL_KEY = 2; - private static final int ICON_TO_SYMBOL_KEY_WITH_SHORTCUT = 3; - private static final int ICON_DELETE_KEY = 4; - private static final int ICON_SETTINGS_KEY = 5; // This is also represented as "@icon/5" in xml. - private static final int ICON_SHORTCUT_KEY = 6; - private static final int ICON_SPACE_KEY = 7; - private static final int ICON_RETURN_KEY = 8; - private static final int ICON_SEARCH_KEY = 9; - private static final int ICON_TAB_KEY = 10; + private static final int ICON_DELETE_KEY = 2; + private static final int ICON_SETTINGS_KEY = 3; // This is also represented as "@icon/3" in XML. + private static final int ICON_SPACE_KEY = 4; + private static final int ICON_RETURN_KEY = 5; + private static final int ICON_SEARCH_KEY = 6; + private static final int ICON_TAB_KEY = 7; // This is also represented as "@icon/7" in XML. + private static final int ICON_SHORTCUT_KEY = 8; + private static final int ICON_SHORTCUT_FOR_LABEL = 9; // This should be aligned with Keyboard.keyIconShifted enum. - private static final int ICON_SHIFTED_SHIFT_KEY = 11; + private static final int ICON_SHIFTED_SHIFT_KEY = 10; // This should be aligned with Keyboard.keyIconPreview enum. - private static final int ICON_PREVIEW_TAB_KEY = 12; - private static final int ICON_PREVIEW_SETTINGS_KEY = 13; - private static final int ICON_PREVIEW_SHORTCUT_KEY = 14; + private static final int ICON_PREVIEW_TAB_KEY = 11; + private static final int ICON_PREVIEW_SETTINGS_KEY = 12; + private static final int ICON_PREVIEW_SHORTCUT_KEY = 13; - private static final int ICON_LAST = 14; + private static final int ICON_LAST = 13; private final Drawable mIcons[] = new Drawable[ICON_LAST + 1]; - private static final int getIconId(int attrIndex) { + private static final int getIconId(final int attrIndex) { switch (attrIndex) { case R.styleable.Keyboard_iconShiftKey: return ICON_SHIFT_KEY; - case R.styleable.Keyboard_iconToSymbolKey: - return ICON_TO_SYMBOL_KEY; - case R.styleable.Keyboard_iconToSymbolKeyWithShortcut: - return ICON_TO_SYMBOL_KEY_WITH_SHORTCUT; case R.styleable.Keyboard_iconDeleteKey: return ICON_DELETE_KEY; case R.styleable.Keyboard_iconSettingsKey: return ICON_SETTINGS_KEY; - case R.styleable.Keyboard_iconShortcutKey: - return ICON_SHORTCUT_KEY; case R.styleable.Keyboard_iconSpaceKey: return ICON_SPACE_KEY; case R.styleable.Keyboard_iconReturnKey: @@ -73,6 +65,10 @@ public class KeyboardIconsSet { return ICON_SEARCH_KEY; case R.styleable.Keyboard_iconTabKey: return ICON_TAB_KEY; + case R.styleable.Keyboard_iconShortcutKey: + return ICON_SHORTCUT_KEY; + case R.styleable.Keyboard_iconShortcutForLabel: + return ICON_SHORTCUT_FOR_LABEL; case R.styleable.Keyboard_iconShiftedShiftKey: return ICON_SHIFTED_SHIFT_KEY; case R.styleable.Keyboard_iconPreviewTabKey: @@ -86,16 +82,14 @@ public class KeyboardIconsSet { } } - public void loadIcons(TypedArray keyboardAttrs) { + public void loadIcons(final TypedArray keyboardAttrs) { final int count = keyboardAttrs.getIndexCount(); for (int i = 0; i < count; i++) { final int attrIndex = keyboardAttrs.getIndex(i); final int iconId = getIconId(attrIndex); if (iconId != ICON_UNDEFINED) { try { - final Drawable icon = keyboardAttrs.getDrawable(attrIndex); - Keyboard.setDefaultBounds(icon); - mIcons[iconId] = icon; + mIcons[iconId] = setDefaultBounds(keyboardAttrs.getDrawable(attrIndex)); } catch (Resources.NotFoundException e) { Log.w(TAG, "Drawable resource for icon #" + iconId + " not found"); } @@ -103,11 +97,18 @@ public class KeyboardIconsSet { } } - public Drawable getIcon(int iconId) { + public Drawable getIcon(final int iconId) { if (iconId == ICON_UNDEFINED) return null; if (iconId < 0 || iconId >= mIcons.length) throw new IllegalArgumentException("icon id is out of range: " + iconId); return mIcons[iconId]; } + + private static Drawable setDefaultBounds(final Drawable icon) { + if (icon != null) { + icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); + } + return icon; + } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java new file mode 100644 index 000000000..4ccaa72d2 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java @@ -0,0 +1,95 @@ +/* + * 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.keyboard.internal; + +import android.graphics.drawable.Drawable; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class KeyboardParams { + public KeyboardId mId; + + public int mOccupiedHeight; + public int mOccupiedWidth; + + public int mHeight; + public int mWidth; + + public int mTopPadding; + public int mBottomPadding; + public int mHorizontalEdgesPadding; + public int mHorizontalCenterPadding; + + public int mDefaultRowHeight; + public int mDefaultKeyWidth; + public int mHorizontalGap; + public int mVerticalGap; + + public boolean mIsRtlKeyboard; + public int mPopupKeyboardResId; + public int mMaxPopupColumn; + + public int GRID_WIDTH; + public int GRID_HEIGHT; + + public final List<Key> mKeys = new ArrayList<Key>(); + public final List<Key> mShiftKeys = new ArrayList<Key>(); + public final Set<Key> mShiftLockKeys = new HashSet<Key>(); + public final Map<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>(); + public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>(); + public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); + + public int mMostCommonKeyWidth = 0; + + public void onAddKey(Key key) { + mKeys.add(key); + updateHistogram(key); + if (key.mCode == Keyboard.CODE_SHIFT) { + mShiftKeys.add(key); + if (key.mSticky) { + mShiftLockKeys.add(key); + } + } + } + + public void addShiftedIcon(Key key, Drawable icon) { + mUnshiftedIcons.put(key, key.getIcon()); + mShiftedIcons.put(key, icon); + } + + private int mMaxCount = 0; + private final Map<Integer, Integer> mHistogram = new HashMap<Integer, Integer>(); + + private void updateHistogram(Key key) { + final Integer width = key.mWidth + key.mHorizontalGap; + final int count = (mHistogram.containsKey(width) ? mHistogram.get(width) : 0) + 1; + mHistogram.put(width, count); + if (count > mMaxCount) { + mMaxCount = count; + mMostCommonKeyWidth = width; + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java index 0cde4e5b5..fd98456a8 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java @@ -21,7 +21,7 @@ import android.util.Log; import com.android.inputmethod.keyboard.KeyboardSwitcher; public class KeyboardShiftState { - private static final String TAG = "KeyboardShiftState"; + private static final String TAG = KeyboardShiftState.class.getSimpleName(); private static final boolean DEBUG = KeyboardSwitcher.DEBUG_STATE; private static final int NORMAL = 0; diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java index 965c679ea..31a291cef 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java @@ -16,8 +16,6 @@ package com.android.inputmethod.keyboard.internal; -import android.content.Context; -import android.content.res.Resources; import android.graphics.Paint; import android.graphics.Rect; @@ -27,26 +25,30 @@ import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MiniKeyboard; import com.android.inputmethod.latin.R; -import java.util.List; - -public class MiniKeyboardBuilder { - private final Resources mRes; - private final MiniKeyboard mKeyboard; +public class MiniKeyboardBuilder extends + KeyboardBuilder<MiniKeyboardBuilder.MiniKeyboardParams> { private final CharSequence[] mPopupCharacters; - private final MiniKeyboardLayoutParams mParams; - /* package */ static class MiniKeyboardLayoutParams { - public final int mKeyWidth; - public final int mRowHeight; - /* package */ final int mTopRowAdjustment; - public final int mNumRows; - public final int mNumColumns; - public final int mLeftKeys; - public final int mRightKeys; // includes default key. - public int mTopPadding; + public static class MiniKeyboardParams extends KeyboardParams { + /* package */ int mTopRowAdjustment; + public int mNumRows; + public int mNumColumns; + public int mLeftKeys; + public int mRightKeys; // includes default key. + + public MiniKeyboardParams() { + super(); + } + + /* package for test */ MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth, + int rowHeight, int coordXInParent, int parentKeyboardWidth) { + super(); + setParameters( + numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, parentKeyboardWidth); + } /** - * The object holding mini keyboard layout parameters. + * Set keyboard parameters of mini keyboard. * * @param numKeys number of keys in this mini keyboard. * @param maxColumns number of maximum columns of this mini keyboard. @@ -54,15 +56,15 @@ public class MiniKeyboardBuilder { * @param rowHeight mini keyboard row height in pixel, including vertical gap. * @param coordXInParent coordinate x of the popup key in parent keyboard. * @param parentKeyboardWidth parent keyboard width in pixel. - * parent keyboard. */ - public MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth, int rowHeight, + public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight, int coordXInParent, int parentKeyboardWidth) { - if (parentKeyboardWidth / keyWidth < maxColumns) + if (parentKeyboardWidth / keyWidth < maxColumns) { throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: " + parentKeyboardWidth + " " + keyWidth + " " + maxColumns); - mKeyWidth = keyWidth; - mRowHeight = rowHeight; + } + mDefaultKeyWidth = keyWidth; + mDefaultRowHeight = rowHeight; final int numRows = (numKeys + maxColumns - 1) / maxColumns; mNumRows = numRows; @@ -108,6 +110,9 @@ public class MiniKeyboardBuilder { } else { mTopRowAdjustment = -1; } + + mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth; + mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; } // Return key position according to column count (0 is default). @@ -160,19 +165,19 @@ public class MiniKeyboardBuilder { } public int getDefaultKeyCoordX() { - return mLeftKeys * mKeyWidth; + return mLeftKeys * mDefaultKeyWidth; } public int getX(int n, int row) { - final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX(); + final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX(); if (isTopRow(row)) { - return x + mTopRowAdjustment * (mKeyWidth / 2); + return x + mTopRowAdjustment * (mDefaultKeyWidth / 2); } return x; } public int getY(int row) { - return (mNumRows - 1 - row) * mRowHeight + mTopPadding; + return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding; } public int getRowFlags(int row) { @@ -185,42 +190,32 @@ public class MiniKeyboardBuilder { private boolean isTopRow(int rowCount) { return rowCount == mNumRows - 1; } - - public void setTopPadding (int topPadding) { - mTopPadding = topPadding; - } - - public int getKeyboardHeight() { - return mNumRows * mRowHeight + mTopPadding; - } - - public int getKeyboardWidth() { - return mNumColumns * mKeyWidth; - } } - public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey, + public MiniKeyboardBuilder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) { - final Context context = view.getContext(); - mRes = context.getResources(); - final MiniKeyboard keyboard = new MiniKeyboard( - context, layoutTemplateResId, parentKeyboard); - mKeyboard = keyboard; + super(view.getContext(), new MiniKeyboardParams()); + load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId)); + + // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal + // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are + // needed to keep having the same horizontal and vertical key spacing. + mParams.mHorizontalGap = 0; + mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; + // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard, + // revert the above hacks and uncomment the following lines. + //mParams.mHorizontalGap = parentKeyboard.mHorizontalGap; + //mParams.mVerticalGap = parentKeyboard.mVerticalGap; + + mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard; mPopupCharacters = parentKey.mPopupCharacters; - final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth()); - final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams( + final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth); + mParams.setParameters( mPopupCharacters.length, parentKey.mMaxPopupColumn, - keyWidth, parentKeyboard.getRowHeight(), - parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2, + keyWidth, parentKeyboard.mDefaultRowHeight, + parentKey.mX + (mParams.mDefaultKeyWidth - keyWidth) / 2, view.getMeasuredWidth()); - params.setTopPadding(keyboard.getVerticalGap()); - mParams = params; - - keyboard.setRowHeight(params.mRowHeight); - keyboard.setKeyboardHeight(params.getKeyboardHeight()); - keyboard.setMinWidth(params.getKeyboardWidth()); - keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2); } private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters, @@ -249,17 +244,16 @@ public class MiniKeyboardBuilder { return Math.max(minKeyWidth, maxWidth + horizontalPadding); } + @Override public MiniKeyboard build() { - final MiniKeyboard keyboard = mKeyboard; - final List<Key> keys = keyboard.getKeys(); - final MiniKeyboardLayoutParams params = mParams; + final MiniKeyboardParams params = mParams; for (int n = 0; n < mPopupCharacters.length; n++) { final CharSequence label = mPopupCharacters[n]; final int row = n / params.mNumColumns; - final Key key = new Key(mRes, keyboard, label, params.getX(n, row), params.getY(row), - params.mKeyWidth, params.mRowHeight, params.getRowFlags(row)); - keys.add(key); + final Key key = new Key(mResources, params, label, params.getX(n, row), params.getY(row), + params.mDefaultKeyWidth, params.mDefaultRowHeight, params.getRowFlags(row)); + params.onAddKey(key); } - return keyboard; + return new MiniKeyboard(params); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java index 8276f5d78..032489e66 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java @@ -23,6 +23,8 @@ import android.util.Log; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; +import java.util.ArrayList; + /** * String parser of popupCharacters attribute of Key. * The string is comma separated texts each of which represents one popup key. @@ -182,4 +184,54 @@ public class PopupCharactersParser { super(message); } } + + public interface CodeFilter { + public boolean shouldFilterOut(int code); + } + + public static final CodeFilter DIGIT_FILTER = new CodeFilter() { + @Override + public boolean shouldFilterOut(int code) { + return Character.isDigit(code); + } + }; + + public static final CodeFilter NON_ASCII_FILTER = new CodeFilter() { + @Override + public boolean shouldFilterOut(int code) { + return code < 0x20 || code > 0x7e; + } + }; + + public static CharSequence[] filterOut(Resources res, CharSequence[] popupCharacters, + CodeFilter filter) { + if (popupCharacters == null || popupCharacters.length < 1) { + return null; + } + if (popupCharacters.length == 1 + && filter.shouldFilterOut(getCode(res, popupCharacters[0].toString()))) { + return null; + } + ArrayList<CharSequence> filtered = null; + for (int i = 0; i < popupCharacters.length; i++) { + final CharSequence popupSpec = popupCharacters[i]; + if (filter.shouldFilterOut(getCode(res, popupSpec.toString()))) { + if (filtered == null) { + filtered = new ArrayList<CharSequence>(); + for (int j = 0; j < i; j++) { + filtered.add(popupCharacters[j]); + } + } + } else if (filtered != null) { + filtered.add(popupSpec); + } + } + if (filtered == null) { + return popupCharacters; + } + if (filtered.size() == 0) { + return null; + } + return filtered.toArray(new CharSequence[filtered.size()]); + } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/Row.java b/java/src/com/android/inputmethod/keyboard/internal/Row.java index b34d6d06f..d53fe12e2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/Row.java +++ b/java/src/com/android/inputmethod/keyboard/internal/Row.java @@ -31,34 +31,19 @@ import com.android.inputmethod.latin.R; */ public class Row { /** Default width of a key in this row. */ - public final int mDefaultWidth; + public final int mDefaultKeyWidth; /** Default height of a key in this row. */ - public final int mDefaultHeight; - /** Default horizontal gap between keys in this row. */ - public final int mDefaultHorizontalGap; - /** Vertical gap following this row. */ - public final int mVerticalGap; + public final int mRowHeight; - private final Keyboard mKeyboard; - - public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) { - this.mKeyboard = keyboard; - final int keyboardWidth = keyboard.getDisplayWidth(); - final int keyboardHeight = keyboard.getKeyboardHeight(); + public Row(Resources res, KeyboardParams params, XmlResourceParser parser) { + final int keyboardWidth = params.mWidth; + final int keyboardHeight = params.mHeight; TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); - mDefaultWidth = KeyboardParser.getDimensionOrFraction(a, - R.styleable.Keyboard_keyWidth, keyboardWidth, keyboard.getKeyWidth()); - mDefaultHeight = KeyboardParser.getDimensionOrFraction(a, - R.styleable.Keyboard_rowHeight, keyboardHeight, keyboard.getRowHeight()); - mDefaultHorizontalGap = KeyboardParser.getDimensionOrFraction(a, - R.styleable.Keyboard_horizontalGap, keyboardWidth, keyboard.getHorizontalGap()); - mVerticalGap = KeyboardParser.getDimensionOrFraction(a, - R.styleable.Keyboard_verticalGap, keyboardHeight, keyboard.getVerticalGap()); + mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(a, + R.styleable.Keyboard_keyWidth, keyboardWidth, params.mDefaultKeyWidth); + mRowHeight = KeyboardBuilder.getDimensionOrFraction(a, + R.styleable.Keyboard_rowHeight, keyboardHeight, params.mDefaultRowHeight); a.recycle(); } - - public Keyboard getKeyboard() { - return mKeyboard; - } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 9748d6006..6a6a0a4ee 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -156,10 +156,11 @@ public class BinaryDictionary extends Dictionary { } } + // proximityInfo may not be null. @Override - public void getWords(final WordComposer codes, final WordCallback callback) { - final int count = getSuggestions(codes, mKeyboardSwitcher.getLatinKeyboard(), - mOutputChars, mScores); + public void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { + final int count = getSuggestions(codes, proximityInfo, mOutputChars, mScores); for (int j = 0; j < count; ++j) { if (mScores[j] < 1) break; @@ -179,8 +180,9 @@ public class BinaryDictionary extends Dictionary { return mNativeDict != 0; } - /* package for test */ int getSuggestions(final WordComposer codes, final Keyboard keyboard, - char[] outputChars, int[] scores) { + // proximityInfo may not be null. + /* package for test */ int getSuggestions(final WordComposer codes, + final ProximityInfo proximityInfo, char[] outputChars, int[] scores) { if (!isValidDictionary()) return -1; final int codesSize = codes.size(); @@ -196,9 +198,8 @@ public class BinaryDictionary extends Dictionary { Arrays.fill(outputChars, (char) 0); Arrays.fill(scores, 0); - final int proximityInfo = keyboard == null ? 0 : keyboard.getProximityInfo(); return getSuggestionsNative( - mNativeDict, proximityInfo, + mNativeDict, proximityInfo.getNativeProximityInfo(), codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, mFlags, outputChars, scores); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 41b577cf3..3da670e2e 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -20,16 +20,18 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; +import android.database.Cursor; import android.net.Uri; import android.text.TextUtils; +import android.util.Log; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; @@ -38,89 +40,83 @@ import java.util.Locale; * file from the dictionary provider */ public class BinaryDictionaryFileDumper { + private static final String TAG = BinaryDictionaryFileDumper.class.getSimpleName(); + /** * The size of the temporary buffer to copy files. */ static final int FILE_READ_BUFFER_SIZE = 1024; + private static final String DICTIONARY_PROJECTION[] = { "id" }; + // Prevents this class to be accidentally instantiated. private BinaryDictionaryFileDumper() { } /** - * Generates a file name that matches the locale passed as an argument. - * The file name is basically the result of the .toString() method, except we replace - * any @File.separator with an underscore to avoid generating a file name that may not - * be created. - * @param locale the locale for which to get the file name - * @param context the context to use for getting the directory - * @return the name of the file to be created + * Return for a given locale or dictionary id the provider URI to get the dictionary. */ - private static String getCacheFileNameForLocale(Locale locale, Context context) { - // The following assumes two things : - // 1. That File.separator is not the same character as "_" - // I don't think any android system will ever use "_" as a path separator - // 2. That no two locales differ by only a File.separator versus a "_" - // Since "_" can't be part of locale components this should be safe. - // Examples: - // en -> en - // en_US_POSIX -> en_US_POSIX - // en__foo/bar -> en__foo_bar - final String[] separator = { File.separator }; - final String[] empty = { "_" }; - final CharSequence basename = TextUtils.replace(locale.toString(), separator, empty); - return context.getFilesDir() + File.separator + basename; + private static Uri getProviderUri(String path) { + return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( + path).build(); } /** - * Return for a given locale the provider URI to query to get the dictionary. + * Queries a content provider for the list of dictionaries for a specific locale + * available to copy into Latin IME. */ - public static Uri getProviderUri(Locale locale) { - return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( - locale.toString()).build(); + private static List<String> getDictIdList(final Locale locale, final Context context) { + final ContentResolver resolver = context.getContentResolver(); + final Uri dictionaryPackUri = getProviderUri(locale.toString()); + + final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); + if (null == c) return Collections.<String>emptyList(); + if (c.getCount() <= 0 || !c.moveToFirst()) { + c.close(); + return Collections.<String>emptyList(); + } + + final List<String> list = new ArrayList<String>(); + do { + final String id = c.getString(0); + if (TextUtils.isEmpty(id)) continue; + list.add(id); + } while (c.moveToNext()); + c.close(); + return list; } /** - * Queries a content provider for dictionary data for some locale and returns the file addresses + * Queries a content provider for dictionary data for some locale and cache the returned files * - * This will query a content provider for dictionary data for a given locale, and return - * the addresses of a file set the members of which are suitable to be mmap'ed. It will copy - * them to local storage if needed. - * It should also check the dictionary versions to avoid unnecessary copies but this is - * still in TODO state. - * This will make the data from the content provider the cached dictionary for this locale, - * overwriting any previous cached data. + * This will query a content provider for dictionary data for a given locale, and copy the + * files locally so that they can be mmap'ed. This may overwrite previously cached dictionaries + * with newer versions if a newer version is made available by the content provider. * @returns the addresses of the files, or null if no data could be obtained. * @throw FileNotFoundException if the provider returns non-existent data. * @throw IOException if the provider-returned data could not be read. */ - public static List<AssetFileAddress> getDictSetFromContentProvider(Locale locale, - Context context) throws FileNotFoundException, IOException { - // TODO: check whether the dictionary is the same or not and if it is, return the cached - // file. - // TODO: This should be able to read a number of files from the dictionary pack, copy - // them all and return them. + public static List<AssetFileAddress> cacheDictionariesFromContentProvider(final Locale locale, + final Context context) throws FileNotFoundException, IOException { final ContentResolver resolver = context.getContentResolver(); - final Uri dictionaryPackUri = getProviderUri(locale); - final AssetFileDescriptor afd = resolver.openAssetFileDescriptor(dictionaryPackUri, "r"); - if (null == afd) return null; - final String fileName = - copyFileTo(afd.createInputStream(), getCacheFileNameForLocale(locale, context)); - afd.close(); - return Arrays.asList(AssetFileAddress.makeFromFileName(fileName)); - } - - /** - * Accepts a file as dictionary data for some locale and returns the name of a file. - * - * This will make the data in the input file the cached dictionary for this locale, overwriting - * any previous cached data. - */ - public static String getDictionaryFileFromFile(String fileName, Locale locale, - Context context) throws FileNotFoundException, IOException { - return copyFileTo(new FileInputStream(fileName), getCacheFileNameForLocale(locale, - context)); + final List<String> idList = getDictIdList(locale, context); + final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>(); + for (String id : idList) { + final Uri wordListUri = getProviderUri(id); + final AssetFileDescriptor afd = + resolver.openAssetFileDescriptor(wordListUri, "r"); + if (null == afd) continue; + final String fileName = copyFileTo(afd.createInputStream(), + BinaryDictionaryGetter.getCacheFileName(id, locale, context)); + afd.close(); + if (0 >= resolver.delete(wordListUri, null, null)) { + // I'd rather not print the word list ID to the log here out of security concerns + Log.e(TAG, "Could not have the dictionary pack delete a word list"); + } + fileAddressList.add(AssetFileAddress.makeFromFileName(fileName)); + } + return fileAddressList; } /** @@ -135,7 +131,9 @@ public class BinaryDictionaryFileDumper { final Locale savedLocale = Utils.setSystemLocale(res, locale); final InputStream stream = res.openRawResource(resource); Utils.setSystemLocale(res, savedLocale); - return copyFileTo(stream, getCacheFileNameForLocale(locale, context)); + return copyFileTo(stream, + BinaryDictionaryGetter.getCacheFileName(Integer.toString(resource), + locale, context)); } /** diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 989a0e9a0..360cf21ca 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -17,13 +17,17 @@ package com.android.inputmethod.latin; import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.util.Log; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -37,10 +41,105 @@ class BinaryDictionaryGetter { */ private static final String TAG = BinaryDictionaryGetter.class.getSimpleName(); + /** + * Name of the common preferences name to know which word list are on and which are off. + */ + private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs"; + // Prevents this from being instantiated private BinaryDictionaryGetter() {} /** + * Returns whether we may want to use this character as part of a file name. + * + * This basically only accepts ascii letters and numbers, and rejects everything else. + */ + private static boolean isFileNameCharacter(int codePoint) { + if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit + if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase + if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase + return codePoint == '_'; // Underscore + } + + /** + * Escapes a string for any characters that may be suspicious for a file or directory name. + * + * Concretely this does a sort of URL-encoding except it will encode everything that's not + * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which + * we cannot allow here) + */ + // TODO: create a unit test for this method + private static String replaceFileNameDangerousCharacters(final String name) { + // This assumes '%' is fully available as a non-separator, normal + // character in a file name. This is probably true for all file systems. + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); ++i) { + final int codePoint = name.codePointAt(i); + if (isFileNameCharacter(codePoint)) { + sb.appendCodePoint(codePoint); + } else { + // 6 digits - unicode is limited to 21 bits + sb.append(String.format((Locale)null, "%%%1$06x", codePoint)); + } + } + return sb.toString(); + } + + /** + * Reverse escaping done by replaceFileNameDangerousCharacters. + */ + private static String getWordListIdFromFileName(final String fname) { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fname.length(); ++i) { + final int codePoint = fname.codePointAt(i); + if ('%' != codePoint) { + sb.appendCodePoint(codePoint); + } else { + final int encodedCodePoint = Integer.parseInt(fname.substring(i + 1, i + 7), 16); + i += 6; + sb.appendCodePoint(encodedCodePoint); + } + } + return sb.toString(); + } + + /** + * Find out the cache directory associated with a specific locale. + */ + private static String getCacheDirectoryForLocale(Locale locale, Context context) { + final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toString()); + final String absoluteDirectoryName = context.getFilesDir() + File.separator + + relativeDirectoryName; + final File directory = new File(absoluteDirectoryName); + if (!directory.exists()) { + if (!directory.mkdirs()) { + Log.e(TAG, "Could not create the directory for locale" + locale); + } + } + return absoluteDirectoryName; + } + + /** + * Generates a file name for the id and locale passed as an argument. + * + * In the current implementation the file name returned will always be unique for + * any id/locale pair, but please do not expect that the id can be the same for + * different dictionaries with different locales. An id should be unique for any + * dictionary. + * The file name is pretty much an URL-encoded version of the id inside a directory + * named like the locale, except it will also escape characters that look dangerous + * to some file systems. + * @param id the id of the dictionary for which to get a file name + * @param locale the locale for which to get the file name + * @param context the context to use for getting the directory + * @return the name of the file to be created + */ + public static String getCacheFileName(String id, Locale locale, Context context) { + final String fileName = replaceFileNameDangerousCharacters(id); + return getCacheDirectoryForLocale(locale, context) + File.separator + fileName; + } + + /** * Returns a file address from a resource, or null if it cannot be opened. */ private static AssetFileAddress loadFallbackResource(final Context context, @@ -60,12 +159,51 @@ class BinaryDictionaryGetter { } /** + * Returns the list of cached files for a specific locale. + * + * @param locale the locale to find the dictionary files for. + * @param context the context on which to open the files upon. + * @return a list of binary dictionary files, which may be null but may not be empty. + */ + private static List<AssetFileAddress> getCachedDictionaryList(final Locale locale, + final Context context) { + final String directoryName = getCacheDirectoryForLocale(locale, context); + final File[] cacheFiles = new File(directoryName).listFiles(); + // TODO: Never return null. Fallback on the built-in dictionary, and if that's + // not present or disabled, then return an empty list. + if (null == cacheFiles) return null; + + final SharedPreferences dictPackSettings; + try { + final String dictPackName = context.getString(R.string.dictionary_pack_package_name); + final Context dictPackContext = context.createPackageContext(dictPackName, 0); + dictPackSettings = dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME, + Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS); + } catch (NameNotFoundException e) { + // The dictionary pack is not installed... + // TODO: fallback on the built-in dict, see the TODO above + Log.e(TAG, "Could not find a dictionary pack"); + return null; + } + + final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>(); + for (File f : cacheFiles) { + final String wordListId = getWordListIdFromFileName(f.getName()); + final boolean isActive = dictPackSettings.getBoolean(wordListId, true); + if (!isActive) continue; + if (f.canRead()) { + fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); + } else { + Log.e(TAG, "Found a cached dictionary file but cannot read it"); + } + } + return fileList.size() > 0 ? fileList : null; + } + + /** * Returns a list of file addresses for a given locale, trying relevant methods in order. * * Tries to get binary dictionaries from various sources, in order: - * - Uses a private method of getting a private dictionaries, as implemented by the - * PrivateBinaryDictionaryGetter class. - * If that fails: * - Uses a content provider to get a public dictionary set, as per the protocol described * in BinaryDictionaryFileDumper. * If that fails: @@ -74,33 +212,28 @@ class BinaryDictionaryGetter { * - Returns null. * @return The address of a valid file, or null. */ - public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context, - int fallbackResId) { - // Try first to query a private package signed the same way for private files. - final List<AssetFileAddress> privateFiles = - PrivateBinaryDictionaryGetter.getDictionaryFiles(locale, context); - if (null != privateFiles) { - return privateFiles; - } else { - try { - // If that was no-go, try to find a publicly exported dictionary. - List<AssetFileAddress> listFromContentProvider = - BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context); - if (null != listFromContentProvider) { - return listFromContentProvider; - } - // If the list is null, fall through and return the fallback - } catch (FileNotFoundException e) { - Log.e(TAG, "Unable to create dictionary file from provider for locale " - + locale.toString() + ": falling back to internal dictionary"); - } catch (IOException e) { - Log.e(TAG, "Unable to read source data for locale " - + locale.toString() + ": falling back to internal dictionary"); + public static List<AssetFileAddress> getDictionaryFiles(final Locale locale, + final Context context, final int fallbackResId) { + try { + // cacheDictionariesFromContentProvider returns the list of files it copied to local + // storage, but we don't really care about what was copied NOW: what we want is the + // list of everything we ever cached, so we ignore the return value. + BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); + List<AssetFileAddress> cachedDictionaryList = getCachedDictionaryList(locale, context); + if (null != cachedDictionaryList) { + return cachedDictionaryList; } - final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, - locale); - if (null == fallbackAsset) return null; - return Arrays.asList(fallbackAsset); + // If the list is null, fall through and return the fallback + } catch (FileNotFoundException e) { + Log.e(TAG, "Unable to create dictionary file from provider for locale " + + locale.toString() + ": falling back to internal dictionary"); + } catch (IOException e) { + Log.e(TAG, "Unable to read source data for locale " + + locale.toString() + ": falling back to internal dictionary"); } + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, + locale); + if (null == fallbackAsset) return null; + return Arrays.asList(fallbackAsset); } } diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 4baf52e52..b5b59ac12 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -38,6 +38,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.PopupWindow; @@ -50,15 +51,12 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; import java.util.List; -public class CandidateView extends LinearLayout implements OnClickListener { - +public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener { public interface Listener { public boolean addWordToDictionary(String word); public void pickSuggestionManually(int index, CharSequence word); } - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); - private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. private static final int MAX_SUGGESTIONS = 18; private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT; @@ -67,11 +65,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { private static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mCandidatesStrip; - private final int mCandidateCountInStrip; - private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; - private final ViewGroup mCandidatesPaneControl; - private final TextView mExpandCandidatesPane; - private final TextView mCloseCandidatesPane; private ViewGroup mCandidatesPane; private ViewGroup mCandidatesPaneContainer; private View mKeyboardView; @@ -80,17 +73,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { private final ArrayList<TextView> mInfos = new ArrayList<TextView>(); private final ArrayList<View> mDividers = new ArrayList<View>(); - private final int mCandidateStripHeight; - private final CharacterStyle mInvertedForegroundColorSpan; - private final CharacterStyle mInvertedBackgroundColorSpan; - private final int mAutoCorrectHighlight; - private static final int AUTO_CORRECT_BOLD = 0x01; - private static final int AUTO_CORRECT_UNDERLINE = 0x02; - private static final int AUTO_CORRECT_INVERT = 0x04; - private final int mColorTypedWord; - private final int mColorAutoCorrect; - private final int mColorSuggestedCandidate; - private final PopupWindow mPreviewPopup; private final TextView mPreviewText; @@ -102,9 +84,9 @@ public class CandidateView extends LinearLayout implements OnClickListener { private boolean mShowingAutoCorrectionInverted; private boolean mShowingAddToDictionary; - private final CandidateViewLayoutParams mParams; - private static final int PUNCTUATIONS_IN_STRIP = 6; - private static final float MIN_TEXT_XSCALE = 0.75f; + private final SuggestionsStripParams mStripParams; + private final SuggestionsPaneParams mPaneParams; + private static final float MIN_TEXT_XSCALE = 0.70f; private final UiHandler mHandler = new UiHandler(this); @@ -157,118 +139,358 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private static class CandidateViewLayoutParams { - public final TextPaint mPaint; + private static class CandidateViewParams { public final int mPadding; public final int mDividerWidth; public final int mDividerHeight; - public final int mControlWidth; - private final int mAutoCorrectHighlight; + public final int mCandidateStripHeight; - public final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + protected final List<TextView> mWords; + protected final List<View> mDividers; + protected final List<TextView> mInfos; - public int mCountInStrip; - // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided - // width without squeezing the text. - public boolean mCanUseFixedWidthColumns; - public int mMaxWidth; - public int mAvailableWidthForWords; - public int mConstantWidthForPaddings; - public int mVariableWidthForWords; - public float mScaleX; + protected CandidateViewParams(List<TextView> words, List<View> dividers, + List<TextView> infos) { + mWords = words; + mDividers = dividers; + mInfos = infos; - public CandidateViewLayoutParams(Resources res, TextView word, View divider, View control, - int autoCorrectHighlight) { - mPaint = new TextPaint(); - final float textSize = res.getDimension(R.dimen.candidate_text_size); - mPaint.setTextSize(textSize); + final TextView word = words.get(0); + final View divider = dividers.get(0); mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); divider.measure(WRAP_CONTENT, MATCH_PARENT); mDividerWidth = divider.getMeasuredWidth(); mDividerHeight = divider.getMeasuredHeight(); - mControlWidth = control.getMeasuredWidth(); - mAutoCorrectHighlight = autoCorrectHighlight; + + final Resources res = word.getResources(); + mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); } + } - public void layoutStrip(SuggestedWords suggestions, int maxWidth, int maxCount) { - final int size = suggestions.size(); - if (size == 0) return; - setupTexts(suggestions, size, mAutoCorrectHighlight); - mCountInStrip = Math.min(maxCount, size); - mScaleX = 1.0f; + private static class SuggestionsPaneParams extends CandidateViewParams { + public SuggestionsPaneParams(List<TextView> words, List<View> dividers, + List<TextView> infos) { + super(words, dividers, infos); + } + + public int layout(SuggestedWords suggestions, ViewGroup paneView, int from, int textColor, + int paneWidth) { + final int count = Math.min(mWords.size(), suggestions.size()); + View centeringFrom = null, lastView = null; + int x = 0, y = 0; + for (int index = from; index < count; index++) { + final int pos = index; + final TextView word = mWords.get(pos); + final View divider = mDividers.get(pos); + final TextPaint paint = word.getPaint(); + word.setTextColor(textColor); + final CharSequence styled = suggestions.getWord(pos); + + final TextView info; + if (DBG) { + final CharSequence debugInfo = getDebugInfo(suggestions, index); + if (debugInfo != null) { + info = mInfos.get(index); + info.setText(debugInfo); + } else { + info = null; + } + } else { + info = null; + } - do { - mMaxWidth = maxWidth; - if (size > mCountInStrip) { - mMaxWidth -= mControlWidth; + final CharSequence text; + final float scaleX; + paint.setTextScaleX(1.0f); + final int textWidth = getTextWidth(styled, paint); + int available = paneWidth - x - mPadding; + if (textWidth >= available) { + // Needs new row, centering previous row. + centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth); + x = 0; + y += mCandidateStripHeight; } + if (x != 0) { + // Add divider if this isn't the left most suggestion in current row. + paneView.addView(divider); + FrameLayoutCompatUtils.placeViewAt(divider, x, y + + (mCandidateStripHeight - mDividerHeight) / 2, mDividerWidth, + mDividerHeight); + x += mDividerWidth; + } + available = paneWidth - x - mPadding; + text = getEllipsizedText(styled, available, paint); + scaleX = paint.getTextScaleX(); + word.setText(text); + word.setTextScaleX(scaleX); + paneView.addView(word); + lastView = word; + if (x == 0) + centeringFrom = word; + word.measure(WRAP_CONTENT, + MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY)); + final int width = word.getMeasuredWidth(); + final int height = word.getMeasuredHeight(); + FrameLayoutCompatUtils.placeViewAt(word, x, y + (mCandidateStripHeight - height) + / 2, width, height); + x += width; + if (info != null) { + paneView.addView(info); + lastView = info; + info.measure(WRAP_CONTENT, WRAP_CONTENT); + final int infoWidth = info.getMeasuredWidth(); + FrameLayoutCompatUtils.placeViewAt(info, x - infoWidth, y, infoWidth, + info.getMeasuredHeight()); + } + } + if (x != 0) { + // Centering last candidates row. + centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth); + } + + return count - from; + } + } + + private static class SuggestionsStripParams extends CandidateViewParams { + private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; + private static final int DEFAULT_CENTER_CANDIDATE_PERCENTILE = 40; + private static final int PUNCTUATIONS_IN_STRIP = 6; + + private final int mColorTypedWord; + private final int mColorAutoCorrect; + private final int mColorSuggestedCandidate; + private final int mCandidateCountInStrip; + private final float mCenterCandidateWeight; + private final int mCenterCandidateIndex; + private final Drawable mMoreCandidateHint; + + private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); + private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); + private final CharacterStyle mInvertedForegroundColorSpan; + private final CharacterStyle mInvertedBackgroundColorSpan; + private static final int AUTO_CORRECT_BOLD = 0x01; + private static final int AUTO_CORRECT_UNDERLINE = 0x02; + private static final int AUTO_CORRECT_INVERT = 0x04; + + private final TextPaint mPaint; + private final int mAutoCorrectHighlight; - tryLayout(); + private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + + public final boolean mAutoCorrectionVisualFlashEnabled; + public boolean mMoreSuggestionsAvailable; + + public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle, + List<TextView> words, List<View> dividers, List<TextView> infos) { + super(words, dividers, infos); + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); + mAutoCorrectionVisualFlashEnabled = a.getBoolean( + R.styleable.CandidateView_autoCorrectionVisualFlashEnabled, false); + mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); + mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); + mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); + mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); + mCandidateCountInStrip = a.getInt( + R.styleable.CandidateView_candidateCountInStrip, + DEFAULT_CANDIDATE_COUNT_IN_STRIP); + mCenterCandidateWeight = a.getInt( + R.styleable.CandidateView_centerCandidatePercentile, + DEFAULT_CENTER_CANDIDATE_PERCENTILE) / 100.0f; + a.recycle(); + + mCenterCandidateIndex = mCandidateCountInStrip / 2; + final Resources res = context.getResources(); + mMoreCandidateHint = res.getDrawable(R.drawable.more_suggestions_hint); + + mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); + mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); - if (mCanUseFixedWidthColumns) { - return; + mPaint = new TextPaint(); + final float textSize = res.getDimension(R.dimen.candidate_text_size); + mPaint.setTextSize(textSize); + } + + public int getTextColor() { + return mColorTypedWord; + } + + private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { + if (!isAutoCorrect) + return word; + final int len = word.length(); + final Spannable spannedWord = new SpannableString(word); + if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) + spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) + spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return spannedWord; + } + + private static boolean willAutoCorrect(SuggestedWords suggestions) { + return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion; + } + + private int getWordPosition(int index, SuggestedWords suggestions) { + // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more + // suggestions. + final int centerPos = willAutoCorrect(suggestions) ? 1 : 0; + if (index == mCenterCandidateIndex) { + return centerPos; + } else if (index == centerPos) { + return mCenterCandidateIndex; + } else { + return index; + } + } + + private int getCandidateTextColor(int index, SuggestedWords suggestions, int pos) { + // TODO: Need to revisit this logic with bigram suggestions + final boolean isSuggestedCandidate = (pos != 0); + + final int color; + if (index == mCenterCandidateIndex && willAutoCorrect(suggestions)) { + color = mColorAutoCorrect; + } else if (isSuggestedCandidate) { + color = mColorSuggestedCandidate; + } else { + color = mColorTypedWord; + } + + final SuggestedWordInfo info = (pos < suggestions.size()) + ? suggestions.getInfo(pos) : null; + if (info != null && info.isPreviousSuggestedWord()) { + return applyAlpha(color, 0.5f); + } else { + return color; + } + } + + private static int applyAlpha(final int color, final float alpha) { + final int newAlpha = (int)(Color.alpha(color) * alpha); + return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + public CharSequence getInvertedText(CharSequence text) { + if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) + return null; + final int len = text.length(); + final Spannable word = new SpannableString(text); + word.setSpan(mInvertedBackgroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + word.setSpan(mInvertedForegroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return word; + } + + public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView, + int stripWidth) { + if (suggestions.isPunctuationSuggestions()) { + return layoutPunctuationSuggestions(suggestions, stripView); + } + + final int countInStrip = mCandidateCountInStrip; + setupTexts(suggestions, countInStrip); + mMoreSuggestionsAvailable = (suggestions.size() > countInStrip); + int x = 0; + for (int index = 0; index < countInStrip; index++) { + final int pos = getWordPosition(index, suggestions); + + if (index != 0) { + final View divider = mDividers.get(pos); + // Add divider if this isn't the left most suggestion in candidate strip. + stripView.addView(divider); } - if (mVariableWidthForWords <= mAvailableWidthForWords) { - return; + + final CharSequence styled = mTexts.get(pos); + final TextView word = mWords.get(pos); + if (index == mCenterCandidateIndex && mMoreSuggestionsAvailable) { + // TODO: This "more suggestions hint" should have nicely designed icon. + word.setCompoundDrawablesWithIntrinsicBounds( + null, null, null, mMoreCandidateHint); + } else { + word.setCompoundDrawables(null, null, null, null); } - final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords; - if (scaleX >= MIN_TEXT_XSCALE) { - mScaleX = scaleX; - return; + // Disable this candidate if the suggestion is null or empty. + word.setEnabled(!TextUtils.isEmpty(styled)); + word.setTextColor(getCandidateTextColor(index, suggestions, pos)); + final int width = getCandidateWidth(index, stripWidth); + final CharSequence text = getEllipsizedText(styled, width, word.getPaint()); + final float scaleX = word.getTextScaleX(); + word.setText(text); // TextView.setText() resets text scale x to 1.0. + word.setTextScaleX(scaleX); + stripView.addView(word); + setLayoutWeight(word, getCandidateWeight(index), mCandidateStripHeight); + + if (DBG) { + final CharSequence debugInfo = getDebugInfo(suggestions, pos); + if (debugInfo != null) { + final TextView info = mInfos.get(pos); + info.setText(debugInfo); + paneView.addView(info); + info.measure(WRAP_CONTENT, WRAP_CONTENT); + final int infoWidth = info.getMeasuredWidth(); + final int y = info.getMeasuredHeight(); + FrameLayoutCompatUtils.placeViewAt(info, x, 0, infoWidth, y); + x += infoWidth * 2; + } } + } + + return countInStrip; + } + + private int getCandidateWidth(int index, int maxWidth) { + final int paddings = mPadding * mCandidateCountInStrip; + final int dividers = mDividerWidth * (mCandidateCountInStrip - 1); + final int availableWidth = maxWidth - paddings - dividers; + return (int)(availableWidth * getCandidateWeight(index)); + } - mCountInStrip--; - } while (mCountInStrip > 1); - } - - public void tryLayout() { - final int maxCount = mCountInStrip; - final int dividers = mDividerWidth * (maxCount - 1); - mConstantWidthForPaddings = dividers + mPadding * maxCount; - mAvailableWidthForWords = mMaxWidth - mConstantWidthForPaddings; - - mPaint.setTextScaleX(mScaleX); - final int maxFixedWidthForWord = (mMaxWidth - dividers) / maxCount - mPadding; - mCanUseFixedWidthColumns = true; - mVariableWidthForWords = 0; - for (int i = 0; i < maxCount; i++) { - final int width = getTextWidth(mTexts.get(i), mPaint); - if (width > maxFixedWidthForWord) - mCanUseFixedWidthColumns = false; - mVariableWidthForWords += width; + private float getCandidateWeight(int index) { + if (index == mCenterCandidateIndex) { + return mCenterCandidateWeight; + } else { + // TODO: Revisit this for cases of 5 or more suggestions + return (1.0f - mCenterCandidateWeight) / (mCandidateCountInStrip - 1); } } - private void setupTexts(SuggestedWords suggestions, int count, int autoCorrectHighlight) { + private void setupTexts(SuggestedWords suggestions, int countInStrip) { mTexts.clear(); - for (int i = 0; i < count; i++) { - final CharSequence suggestion = suggestions.getWord(i); - if (suggestion == null) { - // Skip an empty suggestion, but we need to add a place-holder for it in order - // to avoid an exception in the loop in updateSuggestions(). - mTexts.add(""); - continue; - } - - final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion - && ((i == 1 && !suggestions.mTypedWordValid) - || (i == 0 && suggestions.mTypedWordValid)); - // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 - // and there are multiple suggestions, such as the default punctuation list. - // TODO: Need to revisit this logic with bigram suggestions - final CharSequence styled = getStyledCandidateWord(suggestion, isAutoCorrect, - autoCorrectHighlight); + final int count = Math.min(suggestions.size(), countInStrip); + for (int pos = 0; pos < count; pos++) { + final CharSequence word = suggestions.getWord(pos); + final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions); + final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect); mTexts.add(styled); } + for (int pos = count; pos < countInStrip; pos++) { + // Make this inactive for touches in layout(). + mTexts.add(null); + } } - @Override - public String toString() { - return String.format( - "count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d", - mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns, - mScaleX, mConstantWidthForPaddings, mVariableWidthForWords); + private int layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) { + final int countInStrip = Math.min(suggestions.size(), PUNCTUATIONS_IN_STRIP); + for (int index = 0; index < countInStrip; index++) { + if (index != 0) { + // Add divider if this isn't the left most suggestion in candidate strip. + stripView.addView(mDividers.get(index)); + } + + final TextView word = mWords.get(index); + word.setEnabled(true); + word.setTextColor(mColorTypedWord); + final CharSequence text = suggestions.getWord(index); + word.setText(text); + word.setTextScaleX(1.0f); + word.setCompoundDrawables(null, null, null, null); + stripView.addView(word); + setLayoutWeight(word, 1.0f, mCandidateStripHeight); + } + mMoreSuggestionsAvailable = false; + return countInStrip; } } @@ -295,18 +517,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable( context, attrs, defStyle, R.style.CandidateViewStyle)); - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); - mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); - mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); - mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); - mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); - mCandidateCountInStrip = a.getInt( - R.styleable.CandidateView_candidateCountInStrip, DEFAULT_CANDIDATE_COUNT_IN_STRIP); - a.recycle(); - - Resources res = context.getResources(); - LayoutInflater inflater = LayoutInflater.from(context); + final LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.candidates_strip, this); mPreviewPopup = new PopupWindow(context); @@ -317,56 +528,26 @@ public class CandidateView extends LinearLayout implements OnClickListener { mPreviewPopup.setBackgroundDrawable(null); mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip); - mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); - for (int i = 0; i < MAX_SUGGESTIONS; i++) { + for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) { final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null); - word.setTag(i); + word.setTag(pos); word.setOnClickListener(this); + word.setOnLongClickListener(this); mWords.add(word); + final View divider = inflater.inflate(R.layout.candidate_divider, null); + divider.setTag(pos); + divider.setOnClickListener(this); + mDividers.add(divider); mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null)); - mDividers.add(inflater.inflate(R.layout.candidate_divider, null)); } mTouchToSave = findViewById(R.id.touch_to_save); mWordToSave = (TextView)findViewById(R.id.word_to_save); mWordToSave.setOnClickListener(this); - mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); - mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); - - final TypedArray keyboardViewAttr = context.obtainStyledAttributes( - attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView); - final Drawable expandBackground = keyboardViewAttr.getDrawable( - R.styleable.KeyboardView_keyBackground); - final Drawable closeBackground = keyboardViewAttr.getDrawable( - R.styleable.KeyboardView_keyBackground); - final int keyTextColor = keyboardViewAttr.getColor( - R.styleable.KeyboardView_keyTextColor, 0xFF000000); - keyboardViewAttr.recycle(); - - mCandidatesPaneControl = (ViewGroup)findViewById(R.id.candidates_pane_control); - mExpandCandidatesPane = (TextView)findViewById(R.id.expand_candidates_pane); - mExpandCandidatesPane.setBackgroundDrawable(expandBackground); - mExpandCandidatesPane.setTextColor(keyTextColor); - mExpandCandidatesPane.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - expandCandidatesPane(); - } - }); - mCloseCandidatesPane = (TextView)findViewById(R.id.close_candidates_pane); - mCloseCandidatesPane.setBackgroundDrawable(closeBackground); - mCloseCandidatesPane.setTextColor(keyTextColor); - mCloseCandidatesPane.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - closeCandidatesPane(); - } - }); - mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT); - - mParams = new CandidateViewLayoutParams(res, - mWords.get(0), mDividers.get(0), mCandidatesPaneControl, mAutoCorrectHighlight); + mStripParams = new SuggestionsStripParams(context, attrs, defStyle, mWords, mDividers, + mInfos); + mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos); } /** @@ -387,7 +568,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { if (suggestions == null) return; mSuggestions = suggestions; - mExpandCandidatesPane.setEnabled(false); if (mShowingAutoCorrectionInverted) { mHandler.postUpdateSuggestions(); } else { @@ -395,181 +575,30 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private static CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect, - int autoCorrectHighlight) { - if (!isAutoCorrect) - return word; - final Spannable spannedWord = new SpannableString(word); - if ((autoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) - spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - if ((autoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) - spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - return spannedWord; - } - - private int getCandidateTextColor(boolean isAutoCorrect, boolean isSuggestedCandidate, - SuggestedWordInfo info) { - final int color; - if (isAutoCorrect) { - color = mColorAutoCorrect; - } else if (isSuggestedCandidate) { - color = mColorSuggestedCandidate; - } else { - color = mColorTypedWord; - } - if (info != null && info.isPreviousSuggestedWord()) { - final int newAlpha = (int)(Color.alpha(color) * 0.5f); - return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); - } else { - return color; - } - } - private void updateSuggestions() { - final SuggestedWords suggestions = mSuggestions; - final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList; - final int paneWidth = getWidth(); - final CandidateViewLayoutParams params = mParams; - clear(); closeCandidatesPane(); - if (suggestions.size() == 0) + if (mSuggestions.size() == 0) return; - params.layoutStrip(suggestions, paneWidth, suggestions.isPunctuationSuggestions() - ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip); - - final int count = Math.min(mWords.size(), suggestions.size()); - if (count <= params.mCountInStrip && !DBG) { - mCandidatesPaneControl.setVisibility(GONE); - } else { - mCandidatesPaneControl.setVisibility(VISIBLE); - mExpandCandidatesPane.setVisibility(VISIBLE); - mExpandCandidatesPane.setEnabled(true); - } - - final int countInStrip = params.mCountInStrip; - View centeringFrom = null, lastView = null; - int x = 0, y = 0, infoX = 0; - for (int i = 0; i < count; i++) { - final int pos; - if (i <= 1) { - final boolean willAutoCorrect = !suggestions.mTypedWordValid - && suggestions.mHasMinimalSuggestion; - pos = willAutoCorrect ? 1 - i : i; - } else { - pos = i; - } - final CharSequence suggestion = suggestions.getWord(pos); - if (suggestion == null) continue; - - final SuggestedWordInfo suggestionInfo = (suggestedWordInfoList != null) - ? suggestedWordInfoList.get(pos) : null; - final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion - && ((pos == 1 && !suggestions.mTypedWordValid) - || (pos == 0 && suggestions.mTypedWordValid)); - // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 - // and there are multiple suggestions, such as the default punctuation list. - // TODO: Need to revisit this logic with bigram suggestions - final boolean isSuggestedCandidate = (pos != 0); - final boolean isPunctuationSuggestions = (suggestion.length() == 1 && count > 1); - - final TextView word = mWords.get(pos); - final TextPaint paint = word.getPaint(); - // TODO: Reorder candidates in strip as appropriate. The center candidate should hold - // the word when space is typed (valid typed word or auto corrected word). - word.setTextColor(getCandidateTextColor(isAutoCorrect, - isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo)); - final CharSequence styled = params.mTexts.get(pos); - - final TextView info; - if (DBG && suggestionInfo != null - && !TextUtils.isEmpty(suggestionInfo.getDebugString())) { - info = mInfos.get(i); - info.setText(suggestionInfo.getDebugString()); - } else { - info = null; - } + final int width = getWidth(); + final int countInStrip = mStripParams.layout( + mSuggestions, mCandidatesStrip, mCandidatesPane, width); + final int countInPane = mPaneParams.layout( + mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width); + } - final CharSequence text; - final float scaleX; - if (i < countInStrip) { - if (i == 0 && params.mCountInStrip == 1) { - text = getEllipsizedText(styled, params.mMaxWidth, paint); - scaleX = paint.getTextScaleX(); - } else { - text = styled; - scaleX = params.mScaleX; - } - word.setText(text); - word.setTextScaleX(scaleX); - if (i != 0) { - // Add divider if this isn't the left most suggestion in candidate strip. - mCandidatesStrip.addView(mDividers.get(i)); - } - mCandidatesStrip.addView(word); - if (params.mCanUseFixedWidthColumns) { - setLayoutWeight(word, 1.0f, mCandidateStripHeight); - } else { - final int width = getTextWidth(text, paint) + params.mPadding; - setLayoutWeight(word, width, mCandidateStripHeight); - } - if (info != null) { - mCandidatesPane.addView(info); - info.measure(WRAP_CONTENT, WRAP_CONTENT); - final int width = info.getMeasuredWidth(); - y = info.getMeasuredHeight(); - FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y); - infoX += width * 2; - } - } else { - paint.setTextScaleX(1.0f); - final int textWidth = getTextWidth(styled, paint); - int available = paneWidth - x - params.mPadding; - if (textWidth >= available) { - // Needs new row, centering previous row. - centeringCandidates(centeringFrom, lastView, x, paneWidth); - x = 0; - y += mCandidateStripHeight; - } - if (x != 0) { - // Add divider if this isn't the left most suggestion in current row. - final View divider = mDividers.get(i); - mCandidatesPane.addView(divider); - FrameLayoutCompatUtils.placeViewAt( - divider, x, y + (mCandidateStripHeight - params.mDividerHeight) / 2, - params.mDividerWidth, params.mDividerHeight); - x += params.mDividerWidth; - } - available = paneWidth - x - params.mPadding; - text = getEllipsizedText(styled, available, paint); - scaleX = paint.getTextScaleX(); - word.setText(text); - word.setTextScaleX(scaleX); - mCandidatesPane.addView(word); - lastView = word; - if (x == 0) centeringFrom = word; - word.measure(WRAP_CONTENT, - MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY)); - final int width = word.getMeasuredWidth(); - final int height = word.getMeasuredHeight(); - FrameLayoutCompatUtils.placeViewAt( - word, x, y + (mCandidateStripHeight - height) / 2, width, height); - x += width; - if (info != null) { - mCandidatesPane.addView(info); - lastView = info; - info.measure(WRAP_CONTENT, WRAP_CONTENT); - final int infoWidth = info.getMeasuredWidth(); - FrameLayoutCompatUtils.placeViewAt( - info, x - infoWidth, y, infoWidth, info.getMeasuredHeight()); + private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { + if (DBG && pos < suggestions.size()) { + final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); + if (wordInfo != null) { + final CharSequence debugInfo = wordInfo.getDebugString(); + if (!TextUtils.isEmpty(debugInfo)) { + return debugInfo; } } } - if (x != 0) { - // Centering last candidates row. - centeringCandidates(centeringFrom, lastView, x, paneWidth); - } + return null; } private static void setLayoutWeight(View v, float weight, int height) { @@ -582,13 +611,13 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private void centeringCandidates(View from, View to, int width, int paneWidth) { - final ViewGroup pane = mCandidatesPane; - final int fromIndex = pane.indexOfChild(from); - final int toIndex = pane.indexOfChild(to); - final int offset = (paneWidth - width) / 2; + private static void centeringCandidates(ViewGroup parent, View from, View to, int width, + int parentWidth) { + final int fromIndex = parent.indexOfChild(from); + final int toIndex = parent.indexOfChild(to); + final int offset = (parentWidth - width) / 2; for (int index = fromIndex; index <= toIndex; index++) { - offsetMargin(pane.getChildAt(index), offset, 0); + offsetMargin(parent.getChildAt(index), offset, 0); } } @@ -604,16 +633,20 @@ public class CandidateView extends LinearLayout implements OnClickListener { private static CharSequence getEllipsizedText(CharSequence text, int maxWidth, TextPaint paint) { + if (text == null) return null; paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); - final float scaleX = Math.min(maxWidth / (float)width, 1.0f); + if (width <= maxWidth) { + return text; + } + final float scaleX = maxWidth / (float)width; if (scaleX >= MIN_TEXT_XSCALE) { paint.setTextScaleX(scaleX); return text; } // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get - // squeezed and ellipsezed text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). + // squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). final CharSequence ellipsized = TextUtils.ellipsize( text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); paint.setTextScaleX(MIN_TEXT_XSCALE); @@ -652,31 +685,33 @@ public class CandidateView extends LinearLayout implements OnClickListener { } private void expandCandidatesPane() { - mExpandCandidatesPane.setVisibility(GONE); - mCloseCandidatesPane.setVisibility(VISIBLE); mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight()); mCandidatesPaneContainer.setVisibility(VISIBLE); mKeyboardView.setVisibility(GONE); } private void closeCandidatesPane() { - mExpandCandidatesPane.setVisibility(VISIBLE); - mCloseCandidatesPane.setVisibility(GONE); mCandidatesPaneContainer.setVisibility(GONE); mKeyboardView.setVisibility(VISIBLE); } + private void toggleCandidatesPane() { + if (mCandidatesPaneContainer.getVisibility() == VISIBLE) { + closeCandidatesPane(); + } else { + expandCandidatesPane(); + } + } + public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { - if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) + if (!mStripParams.mAutoCorrectionVisualFlashEnabled) { + return; + } + final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord); + if (inverted == null) return; final TextView tv = mWords.get(1); - final Spannable word = new SpannableString(autoCorrectedWord); - final int wordLength = word.length(); - word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - tv.setText(word); + tv.setText(inverted); mShowingAutoCorrectionInverted = true; } @@ -688,7 +723,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { mWordToSave.setText(word); mShowingAddToDictionary = true; mCandidatesStrip.setVisibility(GONE); - mCandidatesPaneControl.setVisibility(GONE); mTouchToSave.setVisibility(VISIBLE); } @@ -721,7 +755,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { return; final TextView previewText = mPreviewText; - previewText.setTextColor(mColorTypedWord); + previewText.setTextColor(mStripParams.mColorTypedWord); previewText.setText(word); previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); @@ -747,6 +781,15 @@ public class CandidateView extends LinearLayout implements OnClickListener { } @Override + public boolean onLongClick(View view) { + if (mStripParams.mMoreSuggestionsAvailable) { + toggleCandidatesPane(); + return true; + } + return false; + } + + @Override public void onClick(View view) { if (view == mWordToSave) { addToDictionary(((TextView)view).getText()); @@ -754,6 +797,11 @@ public class CandidateView extends LinearLayout implements OnClickListener { return; } + if (view == mCandidatesPane) { + closeCandidatesPane(); + return; + } + final Object tag = view.getTag(); if (!(tag instanceof Integer)) return; diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java index 66a041508..8a7dfb839 100644 --- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java @@ -49,20 +49,28 @@ public class ContactsDictionary extends ExpandableDictionary { private long mLastLoadedContacts; - public ContactsDictionary(Context context, int dicTypeId) { + 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) { + Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) { @Override public void onChange(boolean self) { setRequiresReload(true); } }); - loadDictionary(); + } + + public void reopen(final Context context) { + registerObserver(context); } @Override diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index c7737b9a2..c35b42877 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -1,12 +1,12 @@ /* * 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 @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.ProximityInfo; + /** * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key * strokes. @@ -25,7 +27,7 @@ public abstract class Dictionary { * Whether or not to replicate the typed word in the suggested list, even if it's valid. */ protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false; - + /** * The weight to give to a word if it's length is the same as the number of typed characters. */ @@ -57,13 +59,15 @@ public abstract class Dictionary { } /** - * Searches for words in the dictionary that match the characters in the composer. Matched + * Searches for words in the dictionary that match the characters in the composer. Matched * words are added through the callback object. * @param composer the key sequence to match * @param callback the callback object to send matched words to as possible candidates + * @param proximityInfo the object for key proximity. May be ignored by some implementations. * @see WordCallback#addWord(char[], int, int, int, int, DataType) */ - abstract public void getWords(final WordComposer composer, final WordCallback callback); + abstract public void getWords(final WordComposer composer, final WordCallback callback, + final ProximityInfo proximityInfo); /** * Searches for pairs in the bigram dictionary that matches the previous word and all the @@ -83,7 +87,7 @@ public abstract class Dictionary { * @return true if the word exists, false otherwise */ abstract public boolean isValidWord(CharSequence word); - + /** * Compares the contents of the character array with the typed word and returns true if they * are the same. diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 107840331..739153044 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -47,9 +49,10 @@ public class DictionaryCollection extends Dictionary { } @Override - public void getWords(final WordComposer composer, final WordCallback callback) { + public void getWords(final WordComposer composer, final WordCallback callback, + final ProximityInfo proximityInfo) { for (final Dictionary dict : mDictionaries) - dict.getWords(composer, callback); + dict.getWords(composer, callback, proximityInfo); } @Override diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 97a4a1816..35d1541ff 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -20,6 +20,7 @@ import android.content.Context; import android.os.AsyncTask; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.ProximityInfo; import java.util.LinkedList; @@ -193,7 +194,8 @@ public class ExpandableDictionary extends Dictionary { } @Override - public void getWords(final WordComposer codes, final WordCallback callback) { + public void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { synchronized (mUpdatingLock) { // If we need to update, start off a background task if (mRequiresReload) startDictionaryLoadingTaskLocked(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d9d421411..a932f03ac 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -64,6 +64,7 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.keyboard.KeyboardSwitcher.KeyboardLayoutState; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.keyboard.LatinKeyboardView; @@ -112,6 +113,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Key events coming any faster than this are long-presses. private static final int QUICK_PRESS = 200; + private static final int SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY = 2; + private static final int ACCUMULATE_START_INPUT_VIEW_DELAY = 20; + private static final int RESTORE_KEYBOARD_STATE_DELAY = 500; + /** * The name of the scheme used by the Package Manager to warn of a new package installation, * replacement or removal. @@ -165,7 +170,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private WordComposer mWordComposer = new WordComposer(); private CharSequence mBestWord; private boolean mHasUncommittedTypedChars; - private boolean mHasDictionary; // Magic space: a space that should disappear on space/apostrophe insertion, move after the // punctuation on punctuation insertion, and become a real space on alpha char insertion. private boolean mJustAddedMagicSpace; // This indicates whether the last char is a magic space. @@ -186,8 +190,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private long mLastKeyTime; private AudioManager mAudioManager; - // Align sound effect volume on music volume - private static final float FX_VOLUME = -1.0f; + private static float mFxVolume = -1.0f; // just a default value to be updated runtime private boolean mSilentModeOn; // System-wide current configuration // TODO: Move this flag to VoiceProxy @@ -218,6 +221,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private static final int MSG_SET_BIGRAM_PREDICTIONS = 7; private static final int MSG_CONFIRM_ORIENTATION_CHANGE = 8; private static final int MSG_START_INPUT_VIEW = 9; + private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 10; private static class OrientationChangeArgs { public final int mOldWidth; @@ -302,6 +306,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar case MSG_START_INPUT_VIEW: latinIme.onStartInputView((EditorInfo)msg.obj, false); break; + case MSG_RESTORE_KEYBOARD_LAYOUT: + removeMessages(MSG_UPDATE_SHIFT_STATE); + ((KeyboardLayoutState)msg.obj).restore(); + break; } } @@ -392,22 +400,38 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return hasMessages(MSG_SPACE_TYPED); } + public void postRestoreKeyboardLayout() { + final LatinIME latinIme = getOuterInstance(); + final KeyboardLayoutState state = latinIme.mKeyboardSwitcher.getKeyboardState(); + if (state.isValid()) { + removeMessages(MSG_RESTORE_KEYBOARD_LAYOUT); + sendMessageDelayed( + obtainMessage(MSG_RESTORE_KEYBOARD_LAYOUT, state), + RESTORE_KEYBOARD_STATE_DELAY); + } + } + private void postConfirmOrientationChange(OrientationChangeArgs args) { removeMessages(MSG_CONFIRM_ORIENTATION_CHANGE); - // Will confirm whether orientation change has finished or not after 2ms again. - sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args), 2); + // Will confirm whether orientation change has finished or not again. + sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args), + SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY); } public void startOrientationChanging(int oldw, int oldh) { postConfirmOrientationChange(new OrientationChangeArgs(oldw, oldh)); + final LatinIME latinIme = getOuterInstance(); + latinIme.mKeyboardSwitcher.getKeyboardState().save(); + postRestoreKeyboardLayout(); } public boolean postStartInputView(EditorInfo attribute) { if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) { removeMessages(MSG_START_INPUT_VIEW); - // Postpone onStartInputView 20ms afterward and see if orientation change has - // finished. - sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute), 20); + // Postpone onStartInputView by ACCUMULATE_START_INPUT_VIEW_DELAY and see if + // orientation change has finished. + sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute), + ACCUMULATE_START_INPUT_VIEW_DELAY); return true; } return false; @@ -484,7 +508,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr()); - resetContactsDictionary(); + resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); + updateSoundEffectVolume(); } private void initSuggest() { @@ -493,8 +518,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final Resources res = mResources; final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale); + final ContactsDictionary oldContactsDictionary; if (mSuggest != null) { + oldContactsDictionary = mSuggest.getContactsDictionary(); mSuggest.close(); + } else { + oldContactsDictionary = null; } int mainDicResId = Utils.getMainDictionaryResourceId(res); @@ -508,7 +537,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mSuggest.setUserDictionary(mUserDictionary); mIsUserDictionaryAvaliable = mUserDictionary.isEnabled(); - resetContactsDictionary(); + resetContactsDictionary(oldContactsDictionary); mUserUnigramDictionary = new UserUnigramDictionary(this, this, localeStr, Suggest.DIC_USER_UNIGRAM); @@ -523,11 +552,36 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar Utils.setSystemLocale(res, savedLocale); } - private void resetContactsDictionary() { - if (null == mSuggest) return; - ContactsDictionary contactsDictionary = mSettingsValues.mUseContactsDict - ? new ContactsDictionary(this, Suggest.DIC_CONTACTS) : null; - mSuggest.setContactsDictionary(contactsDictionary); + /** + * 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. + * + * @param oldContactsDictionary an optional dictionary to use, or null + */ + private void resetContactsDictionary(final ContactsDictionary oldContactsDictionary) { + final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict); + + final ContactsDictionary 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. + oldContactsDictionary.reopen(this); + dictionaryToUse = oldContactsDictionary; + } else { + dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS); + } + + if (null != mSuggest) { + mSuggest.setContactsDictionary(dictionaryToUse); + } } /* package private */ void resetSuggestMainDict() { @@ -557,7 +611,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // If orientation changed while predicting, commit the change if (conf.orientation != mDisplayOrientation) { mHandler.startOrientationChanging(mDisplayWidth, mDisplayHeight); - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); commitTyped(ic); if (ic != null) ic.finishComposingText(); // For voice input if (isShowingOptionDialog()) @@ -596,6 +650,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void onStartInputView(EditorInfo attribute, boolean restarting) { + mHandler.postRestoreKeyboardLayout(); if (mHandler.postStartInputView(attribute)) { return; } @@ -649,7 +704,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mSubtypeSwitcher.isKeyboardMode()) { switcher.loadKeyboard(attribute, mSettingsValues); - switcher.updateShiftState(); } if (mCandidateView != null) @@ -658,8 +712,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Delay updating suggestions because keyboard input view may not be shown at this point. mHandler.postUpdateSuggestions(); - updateCorrectionMode(); - inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn, mSettingsValues.mKeyPreviewPopupDismissDelay); inputView.setProximityCorrectionEnabled(true); @@ -738,7 +790,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar super.onFinishInput(); LatinImeLogger.commit(); - mKeyboardSwitcher.onAutoCorrectionStateChanged(false); mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging); @@ -751,6 +802,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void onFinishInputView(boolean finishingInput) { super.onFinishInputView(finishingInput); + mKeyboardSwitcher.onFinishInputView(); KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.cancelAllMessages(); // Remove pending messages related to update suggestions @@ -789,7 +841,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final boolean selectionChanged = (newSelStart != candidatesEnd || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart; final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1; - if (((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars) + if (!mExpectingUpdateSelection + && ((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars) || mVoiceProxy.isVoiceInputHighlighted()) && (selectionChanged || candidatesCleared)) { if (candidatesCleared) { @@ -807,7 +860,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar setPunctuationSuggestions(); } TextEntryState.reset(); - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.finishComposingText(); } @@ -872,7 +925,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void hideWindow() { LatinImeLogger.commit(); - mKeyboardSwitcher.onAutoCorrectionStateChanged(false); + mKeyboardSwitcher.onHideWindow(); if (TRACE) Debug.stopMethodTracing(); if (mOptionsDialog != null && mOptionsDialog.isShowing()) { @@ -917,7 +970,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (onEvaluateInputViewShown() && mCandidateViewContainer != null) { final boolean shouldShowCandidates = shown && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true); - if (isExtractViewShown()) { + if (isFullscreenMode()) { // No need to have extra space to show the key preview. mCandidateViewContainer.setMinimumHeight(0); mCandidateViewContainer.setVisibility( @@ -1012,7 +1065,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar event.getAction(), event.getKeyCode(), event.getRepeatCount(), event.getDeviceId(), event.getScanCode(), KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON); - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic != null) ic.sendKeyEvent(newEvent); return true; @@ -1022,12 +1075,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return super.onKeyUp(keyCode, event); } - public void commitTyped(InputConnection inputConnection) { + public void commitTyped(final InputConnection ic) { if (!mHasUncommittedTypedChars) return; mHasUncommittedTypedChars = false; if (mComposingStringBuilder.length() > 0) { - if (inputConnection != null) { - inputConnection.commitText(mComposingStringBuilder, 1); + if (ic != null) { + ic.commitText(mComposingStringBuilder, 1); } mCommittedLength = mComposingStringBuilder.length(); TextEntryState.acceptedTyped(mComposingStringBuilder); @@ -1038,7 +1091,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public boolean getCurrentAutoCapsState() { - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); EditorInfo ei = getCurrentInputEditorInfo(); if (mSettingsValues.mAutoCap && ic != null && ei != null && ei.inputType != InputType.TYPE_NULL) { @@ -1062,25 +1115,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - private static boolean canBeFollowedByPeriod(final int codePoint) { - // TODO: Check again whether there really ain't a better way to check this. - // TODO: This should probably be language-dependant... - return Character.isLetterOrDigit(codePoint) - || codePoint == Keyboard.CODE_SINGLE_QUOTE - || codePoint == Keyboard.CODE_DOUBLE_QUOTE - || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS - || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET - || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET - || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET; - } - private void maybeDoubleSpace() { if (mCorrectionMode == Suggest.CORRECTION_NONE) return; final InputConnection ic = getCurrentInputConnection(); if (ic == null) return; final CharSequence lastThree = ic.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 - && canBeFollowedByPeriod(lastThree.charAt(0)) + && Utils.canBeFollowedByPeriod(lastThree.charAt(0)) && lastThree.charAt(1) == Keyboard.CODE_SPACE && lastThree.charAt(2) == Keyboard.CODE_SPACE && mHandler.isAcceptingDoubleSpaces()) { @@ -1096,10 +1137,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - private void maybeRemovePreviousPeriod(CharSequence text) { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - + // "ic" must not null + private void maybeRemovePreviousPeriod(final InputConnection ic, CharSequence text) { // When the text's first character is '.', remove the previous period // if there is one. CharSequence lastOne = ic.getTextBeforeCursor(1, 0); @@ -1139,25 +1178,31 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } private void onSettingsKeyPressed() { - if (isShowingOptionDialog()) - return; + if (isShowingOptionDialog()) return; if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) { showSubtypeSelectorAndSettings(); - } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) { + } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, false /* exclude aux subtypes */)) { showOptionsMenu(); } else { launchSettings(); } } - private void onSettingsKeyLongPressed() { - if (!isShowingOptionDialog()) { - if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) { + // Virtual codes representing custom requests. These are used in onCustomRequest() below. + public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1; + + @Override + public boolean onCustomRequest(int requestCode) { + if (isShowingOptionDialog()) return false; + switch (requestCode) { + case CODE_SHOW_INPUT_METHOD_PICKER: + if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, true /* include aux subtypes */)) { mImm.showInputMethodPicker(); - } else { - launchSettings(); + return true; } + return false; } + return false; } private boolean isShowingOptionDialog() { @@ -1201,9 +1246,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar case Keyboard.CODE_SETTINGS: onSettingsKeyPressed(); break; - case Keyboard.CODE_SETTINGS_LONGPRESS: - onSettingsKeyLongPressed(); - break; case Keyboard.CODE_CAPSLOCK: switcher.toggleCapsLock(); break; @@ -1238,12 +1280,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void onTextInput(CharSequence text) { mVoiceProxy.commitVoiceInput(); - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic == null) return; mRecorrection.abortRecorrection(false); ic.beginBatchEdit(); commitTyped(ic); - maybeRemovePreviousPeriod(text); + maybeRemovePreviousPeriod(ic, text); ic.commitText(text, 1); ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); @@ -1293,14 +1335,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar TextEntryState.backspace(); if (TextEntryState.isUndoCommit()) { - revertLastWord(deleteChar); + revertLastWord(ic); ic.endBatchEdit(); return; } if (justReplacedDoubleSpace) { - if (revertDoubleSpace()) { - ic.endBatchEdit(); - return; + if (revertDoubleSpace(ic)) { + ic.endBatchEdit(); + return; } } @@ -1315,7 +1357,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // different behavior only in the case of picking the first // suggestion (typed word). It's intentional to have made this // inconsistent with backspacing after selecting other suggestions. - revertLastWord(true /* deleteChar */); + revertLastWord(ic); } else { sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); if (mDeleteCount > DELETE_ACCELERATE_AT) { @@ -1361,7 +1403,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } int code = primaryCode; - if (isAlphabet(code) && isSuggestionsRequested() && !isCursorTouchingWord()) { + if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) + && isSuggestionsRequested() && !isCursorTouchingWord()) { if (!mHasUncommittedTypedChars) { mHasUncommittedTypedChars = true; mComposingStringBuilder.setLength(0); @@ -1398,7 +1441,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } mComposingStringBuilder.append((char) code); mWordComposer.add(code, keyCodes, x, y); - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic != null) { // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { @@ -1443,7 +1486,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // in Italian dov' should not be expanded to dove' because the elision // requires the last vowel to be removed. final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputTypeNoAutoCorrect && mHasDictionary; + && !mInputTypeNoAutoCorrect; if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { pickedDefault = pickDefaultSuggestion(primaryCode); } else { @@ -1586,7 +1629,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } // getSuggestedWordBuilder handles gracefully a null value of prevWord final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( - mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord); + mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord, + mKeyboardSwitcher.getLatinKeyboard().getProximityInfo()); boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); final CharSequence typedWord = wordComposer.getTypedWord(); @@ -1663,15 +1707,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mSettingsValues.mWordSeparators); final boolean recorrecting = TextEntryState.isRecorrecting(); - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.beginBatchEdit(); } if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { - CompletionInfo ci = mApplicationSpecifiedCompletions[index]; if (ic != null) { - ic.commitCompletion(ci); + final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; + ic.commitCompletion(completionInfo); } mCommittedLength = suggestion.length(); if (mCandidateView != null) { @@ -1698,7 +1742,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final int rawPrimaryCode = suggestion.charAt(0); // Maybe apply the "bidi mirrored" conversions for parentheses final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); - final int primaryCode = keyboard.isRtlKeyboard() + final int primaryCode = keyboard.mIsRtlKeyboard ? Key.getRtlParenthesisCode(rawPrimaryCode) : rawPrimaryCode; final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : ""; @@ -1737,9 +1781,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar sendMagicSpace(); } - // We should show the hint if the user pressed the first entry AND either: + // We should show the "Touch again to save" hint if the user pressed the first entry + // AND either: // - There is no dictionary (we know that because we tried to load it => null != mSuggest - // AND mHasDictionary is false) + // AND mSuggest.hasMainDictionary() is false) // - There is a dictionary and the word is not in it // Please note that if mSuggest is null, it means that everything is off: suggestion // and correction, so we shouldn't try to show the hint @@ -1747,7 +1792,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // to do with the autocorrection setting. final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null // If there is no dictionary the hint should be shown. - && (!mHasDictionary + && (!mSuggest.hasMainDictionary() // If "suggestion" is not in the dictionary, the hint should be shown. || !AutoCorrection.isValidWord( mSuggest.getUnigramDictionaries(), suggestion, true)); @@ -1783,10 +1828,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar * Commits the chosen word to the text field and saves it for later retrieval. */ private void commitBestWord(CharSequence bestWord) { - KeyboardSwitcher switcher = mKeyboardSwitcher; + final KeyboardSwitcher switcher = mKeyboardSwitcher; if (!switcher.isKeyboardAvailable()) return; - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic != null) { mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators); SuggestedWords suggestedWords = mCandidateView.getSuggestions(); @@ -1811,7 +1856,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(), mSettingsValues.mWordSeparators); SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( - mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord); + mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord, + mKeyboardSwitcher.getLatinKeyboard().getProximityInfo()); if (builder.size() > 0) { // Explicitly supply an empty typed word (the no-second-arg version of @@ -1878,7 +1924,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public boolean isCursorTouchingWord() { - InputConnection ic = getCurrentInputConnection(); + final InputConnection ic = getCurrentInputConnection(); if (ic == null) return false; CharSequence toLeft = ic.getTextBeforeCursor(1, 0); CharSequence toRight = ic.getTextAfterCursor(1, 0); @@ -1895,36 +1941,34 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return false; } - private boolean sameAsTextBeforeCursor(InputConnection ic, CharSequence text) { + // "ic" must not null + private boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) { CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0); return TextUtils.equals(text, beforeText); } - private void revertLastWord(boolean deleteChar) { + // "ic" must not null + private void revertLastWord(final InputConnection ic) { if (mHasUncommittedTypedChars || mComposingStringBuilder.length() <= 0) { sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); return; } - final InputConnection ic = getCurrentInputConnection(); - final CharSequence punctuation = ic.getTextBeforeCursor(1, 0); - if (deleteChar) ic.deleteSurroundingText(1, 0); + final CharSequence separator = ic.getTextBeforeCursor(1, 0); + ic.deleteSurroundingText(1, 0); final CharSequence textToTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0); - final int toDeleteLength = (!TextUtils.isEmpty(textToTheLeft) - && mSettingsValues.isWordSeparator(textToTheLeft.charAt(0))) - ? mCommittedLength - 1 : mCommittedLength; - ic.deleteSurroundingText(toDeleteLength, 0); - - // Re-insert punctuation only when the deleted character was word separator and the - // composing text wasn't equal to the auto-corrected text. - if (deleteChar - && !TextUtils.isEmpty(punctuation) - && mSettingsValues.isWordSeparator(punctuation.charAt(0)) + ic.deleteSurroundingText(mCommittedLength, 0); + + // Re-insert "separator" only when the deleted character was word separator and the + // composing text wasn't equal to the auto-corrected text which can be found before + // the cursor. + if (!TextUtils.isEmpty(separator) + && mSettingsValues.isWordSeparator(separator.charAt(0)) && !TextUtils.equals(mComposingStringBuilder, textToTheLeft)) { ic.commitText(mComposingStringBuilder, 1); TextEntryState.acceptedTyped(mComposingStringBuilder); - ic.commitText(punctuation, 1); - TextEntryState.typedCharacter(punctuation.charAt(0), true, + ic.commitText(separator, 1); + TextEntryState.typedCharacter(separator.charAt(0), true, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); // Clear composing text mComposingStringBuilder.setLength(0); @@ -1937,9 +1981,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.postUpdateSuggestions(); } - private boolean revertDoubleSpace() { + // "ic" must not null + private boolean revertDoubleSpace(final InputConnection ic) { mHandler.cancelDoubleSpacesTimer(); - final InputConnection ic = getCurrentInputConnection(); // 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); @@ -1978,16 +2022,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues); initSuggest(); loadSettings(); - mKeyboardSwitcher.updateShiftState(); } @Override public void onPress(int primaryCode, boolean withSliding) { - if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) { + final KeyboardSwitcher switcher = mKeyboardSwitcher; + switcher.registerWindowWidth(); + if (switcher.isVibrateAndSoundFeedbackRequired()) { vibrate(); playKeyClick(primaryCode); } - KeyboardSwitcher switcher = mKeyboardSwitcher; final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) { switcher.onPressShift(withSliding); @@ -2024,14 +2068,24 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } }; + // update sound effect volume + private void updateSoundEffectVolume() { + if (mAudioManager == null) { + mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if (mAudioManager == null) return; + } + // This aligns with the current media volume minus 6dB + mFxVolume = (float) mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + / (float) mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 4.0f; + } + // update flags for silent mode private void updateRingerMode() { if (mAudioManager == null) { mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if (mAudioManager == null) return; } - if (mAudioManager != null) { - mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL); - } + mSilentModeOn = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL); } private void playKeyClick(int primaryCode) { @@ -2043,8 +2097,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } if (isSoundOn()) { - // FIXME: Volume and enable should come from UI settings - // FIXME: These should be triggered after auto-repeat logic int sound = AudioManager.FX_KEYPRESS_STANDARD; switch (primaryCode) { case Keyboard.CODE_DELETE: @@ -2057,7 +2109,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar sound = AudioManager.FX_KEYPRESS_SPACEBAR; break; } - mAudioManager.playSoundEffect(sound, FX_VOLUME); + mAudioManager.playSoundEffect(sound, mFxVolume); } } @@ -2083,9 +2135,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void updateCorrectionMode() { // TODO: cleanup messy flags - mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false; final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputTypeNoAutoCorrect && mHasDictionary; + && !mInputTypeNoAutoCorrect; mCorrectionMode = (shouldAutoCorrect && mSettingsValues.mAutoCorrectEnabled) ? Suggest.CORRECTION_FULL : (shouldAutoCorrect ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE); @@ -2122,14 +2173,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } protected void launchSettings() { - launchSettings(Settings.class); + launchSettingsClass(Settings.class); } public void launchDebugSettings() { - launchSettings(DebugSettings.class); + launchSettingsClass(DebugSettings.class); } - protected void launchSettings(Class<? extends PreferenceActivity> settingsClass) { + protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) { handleClose(); Intent intent = new Intent(); intent.setClass(LatinIME.this, settingsClass); @@ -2163,8 +2214,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } }; final AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setIcon(R.drawable.ic_dialog_keyboard) - .setNegativeButton(android.R.string.cancel, null) .setItems(items, listener) .setTitle(title); showOptionDialogInternal(builder.create()); @@ -2191,8 +2240,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } }; final AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setIcon(R.drawable.ic_dialog_keyboard) - .setNegativeButton(android.R.string.cancel, null) .setItems(items, listener) .setTitle(title); showOptionDialogInternal(builder.create()); diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index e44ae29d9..4c2627be3 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -61,7 +61,7 @@ public class Settings extends InputMethodSettingsActivity public static final String PREF_KEY_PREVIEW_POPUP_ON = "popup_on"; public static final String PREF_RECORRECTION_ENABLED = "recorrection_enabled"; public static final String PREF_AUTO_CAP = "auto_cap"; - public static final String PREF_SETTINGS_KEY = "settings_key"; + public static final String PREF_SHOW_SETTINGS_KEY = "show_settings_key"; public static final String PREF_VOICE_SETTINGS_KEY = "voice_mode"; public static final String PREF_INPUT_LANGUAGE = "input_language"; public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; @@ -103,6 +103,7 @@ public class Settings extends InputMethodSettingsActivity public final String mMagicSpaceSwappers; public final String mSuggestPuncs; public final SuggestedWords mSuggestPuncList; + private final String mSymbolsExcludedFromWordSeparators; // From preferences: public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn) @@ -118,6 +119,7 @@ public class Settings extends InputMethodSettingsActivity public final boolean mBigramPredictionEnabled; public final boolean mUseContactsDict; + private final boolean mShowSettingsKey; private final boolean mVoiceKeyEnabled; private final boolean mVoiceKeyOnMain; @@ -151,10 +153,13 @@ public class Settings extends InputMethodSettingsActivity mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols); String wordSeparators = mMagicSpaceStrippers + mMagicSpaceSwappers + res.getString(R.string.magic_space_promoting_symbols); - final String notWordSeparators = res.getString(R.string.non_word_separator_symbols); - for (int i = notWordSeparators.length() - 1; i >= 0; --i) { - wordSeparators = wordSeparators.replace(notWordSeparators.substring(i, i + 1), ""); + final String symbolsExcludedFromWordSeparators = + res.getString(R.string.symbols_excluded_from_word_separators); + for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) { + wordSeparators = wordSeparators.replace( + symbolsExcludedFromWordSeparators.substring(i, i + 1), ""); } + mSymbolsExcludedFromWordSeparators = symbolsExcludedFromWordSeparators; mWordSeparators = wordSeparators; mSuggestPuncs = res.getString(R.string.suggested_punctuations); // TODO: it would be nice not to recreate this each time we change the configuration @@ -165,21 +170,20 @@ public class Settings extends InputMethodSettingsActivity mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false); mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, res.getBoolean(R.bool.config_default_sound_enabled)); - mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res); mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res); mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); - mAutoCorrectEnabled = isAutoCorrectEnabled(prefs, res); mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled); mBigramPredictionEnabled = mBigramSuggestionEnabled && isBigramPredictionEnabled(prefs, res); - mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res); - mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); - + final boolean defaultShowSettingsKey = res.getBoolean( + R.bool.config_default_show_settings_key); + mShowSettingsKey = prefs.getBoolean(Settings.PREF_SHOW_SETTINGS_KEY, + defaultShowSettingsKey); final String voiceModeMain = res.getString(R.string.voice_mode_main); final String voiceModeOff = res.getString(R.string.voice_mode_off); final String voiceMode = prefs.getString(PREF_VOICE_SETTINGS_KEY, voiceModeMain); @@ -197,6 +201,10 @@ public class Settings extends InputMethodSettingsActivity return mWordSeparators.contains(String.valueOf((char)code)); } + public boolean isSymbolExcludedFromWordSeparators(int code) { + return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code)); + } + public boolean isMagicSpaceStripper(int code) { return mMagicSpaceStrippers.contains(String.valueOf((char)code)); } @@ -284,6 +292,10 @@ public class Settings extends InputMethodSettingsActivity return builder.setIsPunctuationSuggestions().build(); } + public boolean isSettingsKeyEnabled(EditorInfo attribute) { + return mShowSettingsKey; + } + public boolean isVoiceKeyEnabled(EditorInfo attribute) { final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); final int inputType = (attribute != null) ? attribute.inputType : 0; @@ -298,9 +310,9 @@ public class Settings extends InputMethodSettingsActivity private PreferenceScreen mInputLanguageSelection; private ListPreference mVoicePreference; - private ListPreference mSettingsKeyPreference; + private CheckBoxPreference mShowSettingsKeyPreference; private ListPreference mShowCorrectionSuggestionsPreference; - private ListPreference mAutoCorrectionThreshold; + private ListPreference mAutoCorrectionThresholdPreference; private ListPreference mKeyPreviewPopupDismissDelay; // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary private CheckBoxPreference mBigramSuggestion; @@ -317,7 +329,7 @@ public class Settings extends InputMethodSettingsActivity private void ensureConsistencyOfAutoCorrectionSettings() { final String autoCorrectionOff = getResources().getString( R.string.auto_correction_threshold_mode_index_off); - final String currentSetting = mAutoCorrectionThreshold.getValue(); + final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff)); mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); } @@ -345,7 +357,7 @@ public class Settings extends InputMethodSettingsActivity mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES); mInputLanguageSelection.setOnPreferenceClickListener(this); mVoicePreference = (ListPreference) findPreference(PREF_VOICE_SETTINGS_KEY); - mSettingsKeyPreference = (ListPreference) findPreference(PREF_SETTINGS_KEY); + mShowSettingsKeyPreference = (CheckBoxPreference) findPreference(PREF_SHOW_SETTINGS_KEY); mShowCorrectionSuggestionsPreference = (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING); SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); @@ -355,7 +367,8 @@ public class Settings extends InputMethodSettingsActivity mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff) .equals(mVoiceModeOff)); - mAutoCorrectionThreshold = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); + mAutoCorrectionThresholdPreference = + (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS); mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS); mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); @@ -376,7 +389,7 @@ public class Settings extends InputMethodSettingsActivity final boolean showSettingsKeyOption = res.getBoolean( R.bool.config_enable_show_settings_key_option); if (!showSettingsKeyOption) { - generalSettings.removePreference(mSettingsKeyPreference); + generalSettings.removePreference(mShowSettingsKeyPreference); } final boolean showVoiceKeyOption = res.getBoolean( @@ -457,7 +470,6 @@ public class Settings extends InputMethodSettingsActivity } else { getPreferenceScreen().removePreference(mVoicePreference); } - updateSettingsKeySummary(); updateShowCorrectionSuggestionsSummary(); updateKeyPreviewPopupDelaySummary(); } @@ -489,7 +501,6 @@ public class Settings extends InputMethodSettingsActivity mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff) .equals(mVoiceModeOff)); updateVoiceModeSummary(); - updateSettingsKeySummary(); updateShowCorrectionSuggestionsSummary(); updateKeyPreviewPopupDelaySummary(); } @@ -513,12 +524,6 @@ public class Settings extends InputMethodSettingsActivity mShowCorrectionSuggestionsPreference.getValue())]); } - private void updateSettingsKeySummary() { - mSettingsKeyPreference.setSummary( - getResources().getStringArray(R.array.settings_key_modes) - [mSettingsKeyPreference.findIndexOfValue(mSettingsKeyPreference.getValue())]); - } - private void updateKeyPreviewPopupDelaySummary() { final ListPreference lp = mKeyPreviewPopupDismissDelay; lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]); diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index f10b1b845..0a391a77e 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -318,7 +318,7 @@ public class SubtypeSwitcher { // when the API level is 10 or previous. mService.notifyOnCurrentInputMethodSubtypeChanged(subtype); } - }.execute(); + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } public Drawable getShortcutIcon() { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index c2452b947..a2d66f398 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -22,6 +22,8 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -86,6 +88,7 @@ public class Suggest implements Dictionary.WordCallback { private AutoCorrection mAutoCorrection; private Dictionary mMainDict; + private ContactsDictionary mContactsDict; private WhitelistDictionary mWhiteListDictionary; private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>(); private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>(); @@ -189,10 +192,16 @@ public class Suggest implements Dictionary.WordCallback { mCorrectionMode = mode; } + // The main dictionary could have been loaded asynchronously. Don't cache the return value + // of this method. public boolean hasMainDictionary() { return mMainDict != null; } + public ContactsDictionary getContactsDictionary() { + return mContactsDict; + } + public Map<String, Dictionary> getUnigramDictionaries() { return mUnigramDictionaries; } @@ -214,7 +223,8 @@ 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(ContactsDictionary contactsDictionary) { + mContactsDict = contactsDictionary; addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); } @@ -263,9 +273,10 @@ public class Suggest implements Dictionary.WordCallback { * @param prevWordForBigram previous word (used only for bigram) * @return suggested words object. */ - public SuggestedWords getSuggestions(View view, WordComposer wordComposer, - CharSequence prevWordForBigram) { - return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build(); + public SuggestedWords getSuggestions(final View view, final WordComposer wordComposer, + final CharSequence prevWordForBigram, final ProximityInfo proximityInfo) { + return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram, + proximityInfo).build(); } private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) { @@ -299,8 +310,9 @@ public class Suggest implements Dictionary.WordCallback { } // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder - public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer, - CharSequence prevWordForBigram) { + public SuggestedWords.Builder getSuggestedWordBuilder(final View view, + final WordComposer wordComposer, CharSequence prevWordForBigram, + final ProximityInfo proximityInfo) { LatinImeLogger.onStartSuggestion(prevWordForBigram); mAutoCorrection.init(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); @@ -365,7 +377,7 @@ public class Suggest implements Dictionary.WordCallback { if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) continue; final Dictionary dictionary = mUnigramDictionaries.get(key); - dictionary.getWords(wordComposer, this); + dictionary.getWords(wordComposer, this, proximityInfo); } } CharSequence autoText = null; diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index b77cbd199..c1c46fa47 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -31,7 +31,7 @@ public class SuggestedWords { public final boolean mTypedWordValid; public final boolean mHasMinimalSuggestion; public final boolean mIsPunctuationSuggestions; - public final List<SuggestedWordInfo> mSuggestedWordInfoList; + private final List<SuggestedWordInfo> mSuggestedWordInfoList; private SuggestedWords(List<CharSequence> words, boolean typedWordValid, boolean hasMinimalSuggestion, boolean isPunctuationSuggestions, @@ -55,6 +55,10 @@ public class SuggestedWords { return mWords.get(pos); } + public SuggestedWordInfo getInfo(int pos) { + return mSuggestedWordInfoList != null ? mSuggestedWordInfoList.get(pos) : null; + } + public boolean hasAutoCorrectionWord() { return mHasMinimalSuggestion && size() > 1 && !mTypedWordValid; } diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index f93d24fe6..6608d8268 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -26,6 +26,8 @@ import android.net.Uri; import android.os.RemoteException; import android.provider.UserDictionary.Words; +import com.android.inputmethod.keyboard.ProximityInfo; + public class UserDictionary extends ExpandableDictionary { private static final String[] PROJECTION_QUERY = { @@ -150,8 +152,9 @@ public class UserDictionary extends ExpandableDictionary { } @Override - public synchronized void getWords(final WordComposer codes, final WordCallback callback) { - super.getWords(codes, callback); + public synchronized void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { + super.getWords(codes, callback, proximityInfo); } @Override diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 6bdc0a857..1a6260a4e 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -111,35 +111,43 @@ public class Utils { } } - public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) { + public static boolean hasMultipleEnabledIMEsOrSubtypes( + final InputMethodManagerCompatWrapper imm, + final boolean shouldIncludeAuxiliarySubtypes) { final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList(); - // Filters out IMEs that have auxiliary subtypes only (including either implicitly or - // explicitly enabled ones). - final ArrayList<InputMethodInfoCompatWrapper> filteredImis = - new ArrayList<InputMethodInfoCompatWrapper>(); + // Number of the filtered IMEs + int filteredImisCount = 0; - outerloop: for (InputMethodInfoCompatWrapper imi : enabledImis) { // We can return true immediately after we find two or more filtered IMEs. - if (filteredImis.size() > 1) return true; + if (filteredImisCount > 1) return true; final List<InputMethodSubtypeCompatWrapper> subtypes = imm.getEnabledInputMethodSubtypeList(imi, true); - // IMEs that have no subtypes should be included. + // IMEs that have no subtypes should be counted. if (subtypes.isEmpty()) { - filteredImis.add(imi); + ++filteredImisCount; continue; } - // IMEs that have one or more non-auxiliary subtypes should be included. + + int auxCount = 0; for (InputMethodSubtypeCompatWrapper subtype : subtypes) { - if (!subtype.isAuxiliary()) { - filteredImis.add(imi); - continue outerloop; + if (subtype.isAuxiliary()) { + ++auxCount; } } + final int nonAuxCount = subtypes.size() - auxCount; + + // IMEs that have one or more non-auxiliary subtypes should be counted. + // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary + // subtypes should be counted as well. + if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { + ++filteredImisCount; + continue; + } } - return filteredImis.size() > 1 + return filteredImisCount > 1 // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled // input method subtype (The current IME should be LatinIME.) || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; @@ -190,6 +198,18 @@ public class Utils { } } + public static boolean canBeFollowedByPeriod(final int codePoint) { + // TODO: Check again whether there really ain't a better way to check this. + // TODO: This should probably be language-dependant... + return Character.isLetterOrDigit(codePoint) + || codePoint == Keyboard.CODE_SINGLE_QUOTE + || codePoint == Keyboard.CODE_DOUBLE_QUOTE + || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS + || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET + || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET + || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET; + } + /* package */ static class RingCharBuffer { private static RingCharBuffer sRingCharBuffer = new RingCharBuffer(); private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; @@ -546,11 +566,11 @@ public class Utils { } } - public static int getKeyboardMode(EditorInfo attribute) { - if (attribute == null) + public static int getKeyboardMode(EditorInfo editorInfo) { + if (editorInfo == null) return KeyboardId.MODE_TEXT; - final int inputType = attribute.inputType; + final int inputType = editorInfo.inputType; final int variation = inputType & InputType.TYPE_MASK_VARIATION; switch (inputType & InputType.TYPE_MASK_CLASS) { @@ -587,11 +607,11 @@ public class Utils { } public static boolean inPrivateImeOptions(String packageName, String key, - EditorInfo attribute) { - if (attribute == null) + EditorInfo editorInfo) { + if (editorInfo == null) return false; return containsInCsv(packageName != null ? packageName + "." + key : key, - attribute.privateImeOptions); + editorInfo.privateImeOptions); } /** diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java index 4377373d2..639c96681 100644 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -21,6 +21,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.util.HashMap; public class WhitelistDictionary extends Dictionary { @@ -89,7 +91,8 @@ public class WhitelistDictionary extends Dictionary { // Not used for WhitelistDictionary. We use getWhitelistedWord() in Suggest.java instead @Override - public void getWords(WordComposer composer, WordCallback callback) { + public void getWords(final WordComposer composer, final WordCallback callback, + final ProximityInfo proximityInfo) { } @Override diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 156510b40..649774d78 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -16,19 +16,183 @@ package com.android.inputmethod.latin.spellcheck; +import android.content.Intent; +import android.content.res.Resources; import android.service.textservice.SpellCheckerService; +import android.service.textservice.SpellCheckerService.Session; +import android.util.Log; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; +import com.android.inputmethod.compat.ArraysCompatUtils; +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.Dictionary.DataType; +import com.android.inputmethod.latin.Dictionary.WordCallback; +import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.WordComposer; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + /** * Service for spell checking, using LatinIME's dictionaries and mechanisms. */ public class AndroidSpellCheckerService extends SpellCheckerService { + private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); + private static final boolean DBG = false; + private static final int POOL_SIZE = 2; + + private final static String[] emptyArray = new String[0]; + private Map<String, DictionaryPool> mDictionaryPools = + Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); + @Override - public SuggestionsInfo getSuggestions(TextInfo textInfo, int suggestionsLimit, - String locale) { - // TODO: implement this - String[] candidates = new String[] {"candidate1", "candidate2", "candidate3"}; - return new SuggestionsInfo(0, candidates); + public Session createSession() { + return new AndroidSpellCheckerSession(); + } + + private static class SuggestionsGatherer implements WordCallback { + private final int DEFAULT_SUGGESTION_LENGTH = 16; + private final String[] mSuggestions; + private final int[] mScores; + private final int mMaxLength; + private int mLength = 0; + + SuggestionsGatherer(final int maxLength) { + mMaxLength = maxLength; + mSuggestions = new String[mMaxLength]; + mScores = new int[mMaxLength]; + } + + @Override + synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score, + int dicTypeId, DataType dataType) { + final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score); + // binarySearch returns the index if the element exists, and -<insertion index> - 1 + // if it doesn't. See documentation for binarySearch. + final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; + + if (mLength < mMaxLength) { + final int copyLen = mLength - insertIndex; + ++mLength; + System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen); + System.arraycopy(mSuggestions, insertIndex, mSuggestions, insertIndex + 1, copyLen); + } else { + if (insertIndex == 0) return true; + System.arraycopy(mScores, 1, mScores, 0, insertIndex); + System.arraycopy(mSuggestions, 1, mSuggestions, 0, insertIndex); + } + mScores[insertIndex] = score; + mSuggestions[insertIndex] = new String(word, wordOffset, wordLength); + + return true; + } + + public String[] getGatheredSuggestions() { + if (0 == mLength) return null; + + final String[] results = new String[mLength]; + for (int i = mLength - 1; i >= 0; --i) { + results[mLength - i - 1] = mSuggestions[i]; + } + return results; + } + } + + @Override + public boolean onUnbind(final Intent intent) { + final Map<String, DictionaryPool> oldPools = mDictionaryPools; + mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); + for (DictionaryPool pool : oldPools.values()) { + pool.close(); + } + return false; + } + + private DictionaryPool getDictionaryPool(final String locale) { + DictionaryPool pool = mDictionaryPools.get(locale); + if (null == pool) { + final Locale localeObject = Utils.constructLocaleFromString(locale); + pool = new DictionaryPool(POOL_SIZE, this, localeObject); + mDictionaryPools.put(locale, pool); + } + return pool; + } + + public DictAndProximity createDictAndProximity(final Locale locale) { + final ProximityInfo proximityInfo = ProximityInfo.createSpellCheckerProximityInfo(); + final Resources resources = getResources(); + final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); + final Dictionary dictionary = + DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId); + return new DictAndProximity(dictionary, proximityInfo); + } + + private class AndroidSpellCheckerSession extends Session { + // Immutable, but need the locale which is not available in the constructor yet + DictionaryPool mDictionaryPool; + + @Override + public void onCreate() { + mDictionaryPool = getDictionaryPool(getLocale()); + } + + // Note : this must be reentrant + /** + * Gets a list of suggestions for a specific string. This returns a list of possible + * corrections for the text passed as an argument. It may split or group words, and + * even perform grammatical analysis. + */ + @Override + public SuggestionsInfo onGetSuggestions(final TextInfo textInfo, + final int suggestionsLimit) { + final String text = textInfo.getText(); + + final SuggestionsGatherer suggestionsGatherer = + new SuggestionsGatherer(suggestionsLimit); + final WordComposer composer = new WordComposer(); + final int length = text.length(); + for (int i = 0; i < length; ++i) { + final int character = text.codePointAt(i); + final int proximityIndex = SpellCheckerProximityInfo.getIndexOf(character); + final int[] proximities; + if (-1 == proximityIndex) { + proximities = new int[] { character }; + } else { + proximities = Arrays.copyOfRange(SpellCheckerProximityInfo.PROXIMITY, + proximityIndex, proximityIndex + SpellCheckerProximityInfo.ROW_SIZE); + } + composer.add(character, proximities, + WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); + } + + boolean isInDict = true; + try { + final DictAndProximity dictInfo = mDictionaryPool.take(); + dictInfo.mDictionary.getWords(composer, suggestionsGatherer, + dictInfo.mProximityInfo); + isInDict = dictInfo.mDictionary.isValidWord(text); + if (!mDictionaryPool.offer(dictInfo)) { + Log.e(TAG, "Can't re-insert a dictionary into its pool"); + } + } catch (InterruptedException e) { + // I don't think this can happen. + return new SuggestionsInfo(0, new String[0]); + } + + final String[] suggestions = suggestionsGatherer.getGatheredSuggestions(); + + final int flags = + (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0) + | (null != suggestions + ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0); + return new SuggestionsInfo(flags, suggestions); + } } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java new file mode 100644 index 000000000..3dbbd40cd --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java @@ -0,0 +1,32 @@ +/* + * 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.spellcheck; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.keyboard.ProximityInfo; + +/** + * A simple container for both a Dictionary and a ProximityInfo. + */ +public class DictAndProximity { + public final Dictionary mDictionary; + public final ProximityInfo mProximityInfo; + public DictAndProximity(final Dictionary dictionary, final ProximityInfo proximityInfo) { + mDictionary = dictionary; + mProximityInfo = proximityInfo; + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java new file mode 100644 index 000000000..ee294f6b0 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -0,0 +1,78 @@ +/* + * 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.spellcheck; + +import android.content.Context; + +import java.util.Locale; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * A blocking queue that creates dictionaries up to a certain limit as necessary. + */ +public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { + private final AndroidSpellCheckerService mService; + private final int mMaxSize; + private final Locale mLocale; + private int mSize; + private volatile boolean mClosed; + + public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service, + final Locale locale) { + super(); + mMaxSize = maxSize; + mService = service; + mLocale = locale; + mSize = 0; + mClosed = false; + } + + @Override + public DictAndProximity take() throws InterruptedException { + final DictAndProximity dict = poll(); + if (null != dict) return dict; + synchronized(this) { + if (mSize >= mMaxSize) { + // Our pool is already full. Wait until some dictionary is ready. + return super.take(); + } else { + ++mSize; + return mService.createDictAndProximity(mLocale); + } + } + } + + public void close() { + synchronized(this) { + mClosed = true; + for (DictAndProximity dict : this) { + dict.mDictionary.close(); + } + clear(); + } + } + + @Override + public boolean offer(final DictAndProximity dict) { + if (mClosed) { + dict.mDictionary.close(); + return false; + } else { + return super.offer(dict); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java deleted file mode 100644 index 63c6d69d7..000000000 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java +++ /dev/null @@ -1,115 +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.spellcheck; - -import android.content.Context; -import android.content.res.Resources; - -import com.android.inputmethod.compat.ArraysCompatUtils; -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.Dictionary.DataType; -import com.android.inputmethod.latin.Dictionary.WordCallback; -import com.android.inputmethod.latin.DictionaryFactory; -import com.android.inputmethod.latin.Utils; -import com.android.inputmethod.latin.WordComposer; - -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; - -/** - * Implements spell checking methods. - */ -public class SpellChecker { - - public final Dictionary mDictionary; - - public SpellChecker(final Context context, final Locale locale) { - final Resources resources = context.getResources(); - final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); - mDictionary = DictionaryFactory.createDictionaryFromManager(context, locale, - fallbackResourceId); - } - - // Note : this must be reentrant - /** - * Finds out whether a word is in the dictionary or not. - * - * @param text the sequence containing the word to check for. - * @param start the index of the first character of the word in text. - * @param end the index of the next-to-last character in text. - * @return true if the word is in the dictionary, false otherwise. - */ - public boolean isCorrect(final CharSequence text, final int start, final int end) { - return mDictionary.isValidWord(text.subSequence(start, end)); - } - - private static class SuggestionsGatherer implements WordCallback { - private final int DEFAULT_SUGGESTION_LENGTH = 16; - private final List<String> mSuggestions = new LinkedList<String>(); - private int[] mScores = new int[DEFAULT_SUGGESTION_LENGTH]; - private int mLength = 0; - - @Override - synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score, - int dicTypeId, DataType dataType) { - if (mLength >= mScores.length) { - final int newLength = mScores.length * 2; - mScores = new int[newLength]; - } - final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score); - // binarySearch returns the index if the element exists, and -<insertion index> - 1 - // if it doesn't. See documentation for binarySearch. - final int insertionIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; - System.arraycopy(mScores, insertionIndex, mScores, insertionIndex + 1, - mLength - insertionIndex); - mLength += 1; - mScores[insertionIndex] = score; - mSuggestions.add(insertionIndex, new String(word, wordOffset, wordLength)); - return true; - } - - public List<String> getGatheredSuggestions() { - return mSuggestions; - } - } - - // Note : this must be reentrant - /** - * Gets a list of suggestions for a specific string. - * - * This returns a list of possible corrections for the text passed as an - * arguments. It may split or group words, and even perform grammatical - * analysis. - * - * @param text the sequence containing the word to check for. - * @param start the index of the first character of the word in text. - * @param end the index of the next-to-last character in text. - * @return a list of possible suggestions to replace the text. - */ - public List<String> getSuggestions(final CharSequence text, final int start, final int end) { - final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(); - final WordComposer composer = new WordComposer(); - for (int i = start; i < end; ++i) { - int character = text.charAt(i); - composer.add(character, new int[] { character }, - WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); - } - mDictionary.getWords(composer, suggestionsGatherer); - return suggestionsGatherer.getGatheredSuggestions(); - } -} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java new file mode 100644 index 000000000..abcf7e52a --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -0,0 +1,94 @@ +/* + * 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.spellcheck; + +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.ProximityInfo; + +import java.util.Map; +import java.util.TreeMap; + +public class SpellCheckerProximityInfo { + final private static int NUL = KeyDetector.NOT_A_CODE; + + // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside + // native code - this value is passed at creation of the binary object and reused + // as the size of the passed array afterwards so they can't be different. + final public static int ROW_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; + + // This is a map from the code point to the index in the PROXIMITY array. + // At the time the native code to read the binary dictionary needs the proximity info be passed + // as a flat array spaced by MAX_PROXIMITY_CHARS_SIZE columns, one for each input character. + // Since we need to build such an array, we want to be able to search in our big proximity data + // quickly by character, and a map is probably the best way to do this. + final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>(); + + // The proximity here is the union of + // - the proximity for a QWERTY keyboard. + // - the proximity for an AZERTY keyboard. + // - the proximity for a QWERTZ keyboard. + // ...plus, add all characters in the ('a', 'e', 'i', 'o', 'u') set to each other. + // + // The reasoning behind this construction is, almost any alphabetic text we may want + // to spell check has been entered with one of the keyboards above. Also, specifically + // to English, many spelling errors consist of the last vowel of the word being wrong + // because in English vowels tend to merge with each other in pronunciation. + final public static int[] PROXIMITY = { + 'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, + 'r', 'e', 'd', 'f', 'g', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 't', 'r', 'f', 'g', 'h', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'y', 't', 'g', 'h', 'j', 'u', 'a', 's', 'd', 'x', NUL, NUL, NUL, NUL, NUL, NUL, + 'u', 'y', 'h', 'j', 'k', 'i', 'a', 'e', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'i', 'u', 'j', 'k', 'l', 'o', 'a', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'o', 'i', 'k', 'l', 'p', 'a', 'e', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'p', 'o', 'l', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + + 'a', 'z', 'x', 's', 'w', 'q', 'e', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, + 's', 'q', 'a', 'z', 'x', 'c', 'd', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, + 'f', 'e', 'd', 'c', 'v', 'b', 'g', 't', 'r', NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'g', 'r', 'f', 'v', 'b', 'n', 'h', 'y', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'h', 't', 'g', 'b', 'n', 'm', 'j', 'u', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'j', 'y', 'h', 'n', 'm', 'k', 'i', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'k', 'u', 'j', 'm', 'l', 'o', 'i', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'l', 'i', 'k', 'p', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + + 'z', 'a', 's', 'd', 'x', 't', 'g', 'h', 'j', 'u', 'q', 'e', NUL, NUL, NUL, NUL, + 'x', 'z', 'a', 's', 'd', 'c', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'c', 'x', 's', 'd', 'f', 'v', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'v', 'c', 'd', 'f', 'g', 'b', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'b', 'v', 'f', 'g', 'h', 'n', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'n', 'b', 'g', 'h', 'j', 'm', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + 'm', 'n', 'h', 'j', 'k', 'l', 'o', 'p', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + }; + static { + for (int i = 0; i < PROXIMITY.length; i += ROW_SIZE) { + if (NUL != PROXIMITY[i]) INDICES.put(PROXIMITY[i], i); + } + } + public static int getIndexOf(int characterCode) { + final Integer result = INDICES.get(characterCode); + if (null == result) return -1; + return result; + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java new file mode 100644 index 000000000..483679a55 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java @@ -0,0 +1,43 @@ +/** + * 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.spellcheck; + +import com.android.inputmethod.latin.R; + +import android.content.Intent; +import android.os.Bundle; +import android.preference.PreferenceActivity; + +import java.util.List; + +/** + * Spell checker preference screen. + */ +public class SpellCheckerSettingsActivity extends PreferenceActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public Intent getIntent() { + final Intent modIntent = new Intent(super.getIntent()); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SpellCheckerSettingsFragment.class.getName()); + modIntent.putExtra(EXTRA_NO_HEADERS, true); + return modIntent; + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java new file mode 100644 index 000000000..9b821be35 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java @@ -0,0 +1,41 @@ +/** + * 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.spellcheck; + +import com.android.inputmethod.latin.R; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +/** + * Preference screen. + */ +public class SpellCheckerSettingsFragment extends PreferenceFragment { + private static final String TAG = SpellCheckerSettingsFragment.class.getSimpleName(); + + /** + * Empty constructor for fragment generation. + */ + public SpellCheckerSettingsFragment() { + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + addPreferencesFromResource(R.xml.spell_checker_settings); + } +} diff --git a/native/Android.mk b/native/Android.mk index bc246a990..f07be6abe 100644 --- a/native/Android.mk +++ b/native/Android.mk @@ -8,15 +8,13 @@ LOCAL_CFLAGS += -Werror -Wall # To suppress compiler warnings for unused variables/functions used for debug features etc. LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function -# Use the new dictionary format -LOCAL_CFLAGS += -DNEW_DICTIONARY_FORMAT - LOCAL_SRC_FILES := \ jni/com_android_inputmethod_keyboard_ProximityInfo.cpp \ jni/com_android_inputmethod_latin_BinaryDictionary.cpp \ jni/jni_common.cpp \ src/bigram_dictionary.cpp \ src/char_utils.cpp \ + src/correction.cpp \ src/dictionary.cpp \ src/proximity_info.cpp \ src/unigram_dictionary.cpp diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp index 6ed4d0982..c340c6c1a 100644 --- a/native/src/bigram_dictionary.cpp +++ b/native/src/bigram_dictionary.cpp @@ -21,13 +21,14 @@ #include "bigram_dictionary.h" #include "dictionary.h" +#include "binary_format.h" namespace latinime { BigramDictionary::BigramDictionary(const unsigned char *dict, int maxWordLength, int maxAlternatives, const bool isLatestDictVersion, const bool hasBigram, Dictionary *parentDictionary) - : DICT(dict), MAX_WORD_LENGTH(maxWordLength), + : DICT(dict + NEW_DICTIONARY_HEADER_SIZE), MAX_WORD_LENGTH(maxWordLength), MAX_ALTERNATIVES(maxAlternatives), IS_LATEST_DICT_VERSION(isLatestDictVersion), HAS_BIGRAM(hasBigram), mParentDictionary(parentDictionary) { if (DEBUG_DICT) { @@ -82,169 +83,64 @@ bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequ return false; } -int BigramDictionary::getBigramAddress(int *pos, bool advance) { - int address = 0; - - address += (DICT[*pos] & 0x3F) << 16; - address += (DICT[*pos + 1] & 0xFF) << 8; - address += (DICT[*pos + 2] & 0xFF); - - if (advance) { - *pos += 3; - } - - return address; -} - -int BigramDictionary::getBigramFreq(int *pos) { - int freq = DICT[(*pos)++] & FLAG_BIGRAM_FREQ; - - return freq; -} - - +/* Parameters : + * prevWord: the word before, the one for which we need to look up bigrams. + * prevWordLength: its length. + * codes: what user typed, in the same format as for UnigramDictionary::getSuggestions. + * codesSize: the size of the codes array. + * bigramChars: an array for output, at the same format as outwords for getSuggestions. + * bigramFreq: an array to output frequencies. + * maxWordLength: the maximum size of a word. + * maxBigrams: the maximum number of bigrams fitting in the bigramChars array. + * maxAlteratives: unused. + * This method returns the number of bigrams this word has, for backward compatibility. + * Note: this is not the number of bigrams output in the array, which is the number of + * bigrams this word has WHOSE first letter also matches the letter the user typed. + * TODO: this may not be a sensible thing to do. It makes sense when the bigrams are + * used to match the first letter of the second word, but once the user has typed more + * and the bigrams are used to boost unigram result scores, it makes little sense to + * reduce their scope to the ones that match the first letter. + */ int BigramDictionary::getBigrams(unsigned short *prevWord, int prevWordLength, int *codes, int codesSize, unsigned short *bigramChars, int *bigramFreq, int maxWordLength, int maxBigrams, int maxAlternatives) { + // TODO: remove unused arguments, and refrain from storing stuff in members of this class + // TODO: have "in" arguments before "out" ones, and make out args explicit in the name mBigramFreq = bigramFreq; mBigramChars = bigramChars; mInputCodes = codes; - mInputLength = codesSize; mMaxBigrams = maxBigrams; - if (HAS_BIGRAM && IS_LATEST_DICT_VERSION) { - int pos = mParentDictionary->getBigramPosition(prevWord, prevWordLength); - if (DEBUG_DICT) { - LOGI("Pos -> %d", pos); - } - if (pos < 0) { - return 0; - } - - int bigramCount = 0; - int bigramExist = (DICT[pos] & FLAG_BIGRAM_READ); - if (bigramExist > 0) { - int nextBigramExist = 1; - while (nextBigramExist > 0 && bigramCount < maxBigrams) { - int bigramAddress = getBigramAddress(&pos, true); - int frequency = (FLAG_BIGRAM_FREQ & DICT[pos]); - // search for all bigrams and store them - searchForTerminalNode(bigramAddress, frequency); - nextBigramExist = (DICT[pos++] & FLAG_BIGRAM_CONTINUED); - bigramCount++; - } - } + const uint8_t* const root = DICT; + int pos = BinaryFormat::getTerminalPosition(root, prevWord, prevWordLength); - return bigramCount; + if (NOT_VALID_WORD == pos) return 0; + const int flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos); + if (0 == (flags & UnigramDictionary::FLAG_HAS_BIGRAMS)) return 0; + if (0 == (flags & UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS)) { + BinaryFormat::getCharCodeAndForwardPointer(root, &pos); + } else { + pos = BinaryFormat::skipOtherCharacters(root, pos); } - return 0; -} - -void BigramDictionary::searchForTerminalNode(int addressLookingFor, int frequency) { - // track word with such address and store it in an array - unsigned short word[MAX_WORD_LENGTH]; - - int pos; - int followDownBranchAddress = DICTIONARY_HEADER_SIZE; - bool found = false; - char followingChar = ' '; - int depth = -1; - - while(!found) { - bool followDownAddressSearchStop = false; - bool firstAddress = true; - bool haveToSearchAll = true; - - if (depth < MAX_WORD_LENGTH && depth >= 0) { - word[depth] = (unsigned short) followingChar; - } - pos = followDownBranchAddress; // pos start at count - int count = DICT[pos] & 0xFF; - if (DEBUG_DICT) { - LOGI("count - %d",count); - } - pos++; - for (int i = 0; i < count; i++) { - // pos at data - pos++; - // pos now at flag - if (!getFirstBitOfByte(&pos)) { // non-terminal - if (!followDownAddressSearchStop) { - int addr = getBigramAddress(&pos, false); - if (addr > addressLookingFor) { - followDownAddressSearchStop = true; - if (firstAddress) { - firstAddress = false; - haveToSearchAll = true; - } else if (!haveToSearchAll) { - break; - } - } else { - followDownBranchAddress = addr; - followingChar = (char)(0xFF & DICT[pos-1]); - if (firstAddress) { - firstAddress = false; - haveToSearchAll = false; - } - } - } - pos += 3; - } else if (getFirstBitOfByte(&pos)) { // terminal - if (addressLookingFor == (pos-1)) { // found !! - depth++; - word[depth] = (0xFF & DICT[pos-1]); - found = true; - break; - } - if (getSecondBitOfByte(&pos)) { // address + freq (4 byte) - if (!followDownAddressSearchStop) { - int addr = getBigramAddress(&pos, false); - if (addr > addressLookingFor) { - followDownAddressSearchStop = true; - if (firstAddress) { - firstAddress = false; - haveToSearchAll = true; - } else if (!haveToSearchAll) { - break; - } - } else { - followDownBranchAddress = addr; - followingChar = (char)(0xFF & DICT[pos-1]); - if (firstAddress) { - firstAddress = false; - haveToSearchAll = true; - } - } - } - pos += 4; - } else { // freq only (2 byte) - pos += 2; - } - - // skipping bigram - int bigramExist = (DICT[pos] & FLAG_BIGRAM_READ); - if (bigramExist > 0) { - int nextBigramExist = 1; - while (nextBigramExist > 0) { - pos += 3; - nextBigramExist = (DICT[pos++] & FLAG_BIGRAM_CONTINUED); - } - } else { - pos++; - } - } + pos = BinaryFormat::skipChildrenPosition(flags, pos); + pos = BinaryFormat::skipFrequency(flags, pos); + int bigramFlags; + int bigramCount = 0; + do { + bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos); + uint16_t bigramBuffer[MAX_WORD_LENGTH]; + const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags, + &pos); + const int length = BinaryFormat::getWordAtAddress(root, bigramPos, MAX_WORD_LENGTH, + bigramBuffer); + + if (checkFirstCharacter(bigramBuffer)) { + const int frequency = UnigramDictionary::MASK_ATTRIBUTE_FREQUENCY & bigramFlags; + addWordBigram(bigramBuffer, length, frequency); } - depth++; - if (followDownBranchAddress == 0) { - if (DEBUG_DICT) { - LOGI("ERROR!!! Cannot find bigram!!"); - } - break; - } - } - if (checkFirstCharacter(word)) { - addWordBigram(word, depth, frequency); - } + ++bigramCount; + } while (0 != (UnigramDictionary::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags)); + return bigramCount; } bool BigramDictionary::checkFirstCharacter(unsigned short *word) { diff --git a/native/src/binary_format.h b/native/src/binary_format.h index a946b1ee3..6f65088db 100644 --- a/native/src/binary_format.h +++ b/native/src/binary_format.h @@ -50,6 +50,8 @@ public: int *pos); static int getTerminalPosition(const uint8_t* const root, const uint16_t* const inWord, const int length); + static int getWordAtAddress(const uint8_t* const root, const int address, const int maxDepth, + uint16_t* outWord); }; inline int BinaryFormat::detectFormat(const uint8_t* const dict) { @@ -290,6 +292,151 @@ inline int BinaryFormat::getTerminalPosition(const uint8_t* const root, } } +// This function searches for a terminal in the dictionary by its address. +// Due to the fact that words are ordered in the dictionary in a strict breadth-first order, +// it is possible to check for this with advantageous complexity. For each node, we search +// for groups with children and compare the children address with the address we look for. +// When we shoot the address we look for, it means the word we look for is in the children +// of the previous group. The only tricky part is the fact that if we arrive at the end of a +// node with the last group's children address still less than what we are searching for, we +// must descend the last group's children (for example, if the word we are searching for starts +// with a z, it's the last group of the root node, so all children addresses will be smaller +// than the address we look for, and we have to descend the z node). +/* Parameters : + * root: the dictionary buffer + * address: the byte position of the last chargroup of the word we are searching for (this is + * what is stored as the "bigram address" in each bigram) + * outword: an array to write the found word, with MAX_WORD_LENGTH size. + * Return value : the length of the word, of 0 if the word was not found. + */ +inline int BinaryFormat::getWordAtAddress(const uint8_t* const root, const int address, + const int maxDepth, uint16_t* outWord) { + int pos = 0; + int wordPos = 0; + + // One iteration of the outer loop iterates through nodes. As stated above, we will only + // traverse nodes that are actually a part of the terminal we are searching, so each time + // we enter this loop we are one depth level further than last time. + // The only reason we count nodes is because we want to reduce the probability of infinite + // looping in case there is a bug. Since we know there is an upper bound to the depth we are + // supposed to traverse, it does not hurt to count iterations. + for (int loopCount = maxDepth; loopCount > 0; --loopCount) { + int lastCandidateGroupPos = 0; + // Let's loop through char groups in this node searching for either the terminal + // or one of its ascendants. + for (int charGroupCount = getGroupCountAndForwardPointer(root, &pos); charGroupCount > 0; + --charGroupCount) { + const int startPos = pos; + const uint8_t flags = getFlagsAndForwardPointer(root, &pos); + const int32_t character = getCharCodeAndForwardPointer(root, &pos); + if (address == startPos) { + // We found the address. Copy the rest of the word in the buffer and return + // the length. + outWord[wordPos] = character; + if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & flags) { + int32_t nextChar = getCharCodeAndForwardPointer(root, &pos); + // We count chars in order to avoid infinite loops if the file is broken or + // if there is some other bug + int charCount = maxDepth; + while (-1 != nextChar && --charCount > 0) { + outWord[++wordPos] = nextChar; + nextChar = getCharCodeAndForwardPointer(root, &pos); + } + } + return ++wordPos; + } + // We need to skip past this char group, so skip any remaining chars after the + // first and possibly the frequency. + if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & flags) { + pos = skipOtherCharacters(root, pos); + } + pos = skipFrequency(flags, pos); + + // The fact that this group has children is very important. Since we already know + // that this group does not match, if it has no children we know it is irrelevant + // to what we are searching for. + const bool hasChildren = (UnigramDictionary::FLAG_GROUP_ADDRESS_TYPE_NOADDRESS != + (UnigramDictionary::MASK_GROUP_ADDRESS_TYPE & flags)); + // We will write in `found' whether we have passed the children address we are + // searching for. For example if we search for "beer", the children of b are less + // than the address we are searching for and the children of c are greater. When we + // come here for c, we realize this is too big, and that we should descend b. + bool found; + if (hasChildren) { + // Here comes the tricky part. First, read the children position. + const int childrenPos = readChildrenPosition(root, flags, pos); + if (childrenPos > address) { + // If the children pos is greater than address, it means the previous chargroup, + // which address is stored in lastCandidateGroupPos, was the right one. + found = true; + } else if (1 >= charGroupCount) { + // However if we are on the LAST group of this node, and we have NOT shot the + // address we should descend THIS node. So we trick the lastCandidateGroupPos + // so that we will descend this node, not the previous one. + lastCandidateGroupPos = startPos; + found = true; + } else { + // Else, we should continue looking. + found = false; + } + } else { + // Even if we don't have children here, we could still be on the last group of this + // node. If this is the case, we should descend the last group that had children, + // and their address is already in lastCandidateGroup. + found = (1 >= charGroupCount); + } + + if (found) { + // Okay, we found the group we should descend. Its address is in + // the lastCandidateGroupPos variable, so we just re-read it. + if (0 != lastCandidateGroupPos) { + const uint8_t lastFlags = + getFlagsAndForwardPointer(root, &lastCandidateGroupPos); + const int32_t lastChar = + getCharCodeAndForwardPointer(root, &lastCandidateGroupPos); + // We copy all the characters in this group to the buffer + outWord[wordPos] = lastChar; + if (UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS & lastFlags) { + int32_t nextChar = + getCharCodeAndForwardPointer(root, &lastCandidateGroupPos); + int charCount = maxDepth; + while (-1 != nextChar && --charCount > 0) { + outWord[++wordPos] = nextChar; + nextChar = getCharCodeAndForwardPointer(root, &lastCandidateGroupPos); + } + } + ++wordPos; + // Now we only need to branch to the children address. Skip the frequency if + // it's there, read pos, and break to resume the search at pos. + lastCandidateGroupPos = skipFrequency(lastFlags, lastCandidateGroupPos); + pos = readChildrenPosition(root, lastFlags, lastCandidateGroupPos); + break; + } else { + // Here is a little tricky part: we come here if we found out that all children + // addresses in this group are bigger than the address we are searching for. + // Should we conclude the word is not in the dictionary? No! It could still be + // one of the remaining chargroups in this node, so we have to keep looking in + // this node until we find it (or we realize it's not there either, in which + // case it's actually not in the dictionary). Pass the end of this group, ready + // to start the next one. + pos = skipChildrenPosAndAttributes(root, flags, pos); + } + } else { + // If we did not find it, we should record the last children address for the next + // iteration. + if (hasChildren) lastCandidateGroupPos = startPos; + // Now skip the end of this group (children pos and the attributes if any) so that + // our pos is after the end of this char group, at the start of the next one. + pos = skipChildrenPosAndAttributes(root, flags, pos); + } + + } + } + // If we have looked through all the chargroups and found no match, the address is + // not the address of a terminal in this dictionary. + return 0; +} + } // namespace latinime #endif // LATINIME_BINARY_FORMAT_H diff --git a/native/src/correction.cpp b/native/src/correction.cpp new file mode 100644 index 000000000..99412b211 --- /dev/null +++ b/native/src/correction.cpp @@ -0,0 +1,597 @@ +/* + * 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. + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#define LOG_TAG "LatinIME: correction.cpp" + +#include "correction.h" +#include "dictionary.h" +#include "proximity_info.h" + +namespace latinime { + +////////////////////// +// inline functions // +////////////////////// +static const char QUOTE = '\''; + +inline bool Correction::isQuote(const unsigned short c) { + const unsigned short userTypedChar = mProximityInfo->getPrimaryCharAt(mInputIndex); + return (c == QUOTE && userTypedChar != QUOTE); +} + +//////////////// +// Correction // +//////////////// + +Correction::Correction(const int typedLetterMultiplier, const int fullWordMultiplier) + : TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier) { +} + +void Correction::initCorrection(const ProximityInfo *pi, const int inputLength, + const int maxDepth) { + mProximityInfo = pi; + mInputLength = inputLength; + mMaxDepth = maxDepth; + mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2; +} + +void Correction::initCorrectionState( + const int rootPos, const int childCount, const bool traverseAll) { + latinime::initCorrectionState(mCorrectionStates, rootPos, childCount, traverseAll); + // TODO: remove + mCorrectionStates[0].mSkipPos = mSkipPos; +} + +void Correction::setCorrectionParams(const int skipPos, const int excessivePos, + const int transposedPos, const int spaceProximityPos, const int missingSpacePos) { + // TODO: remove + mSkipPos = skipPos; + // TODO: remove + mCorrectionStates[0].mSkipPos = skipPos; + mExcessivePos = excessivePos; + mTransposedPos = transposedPos; + mSpaceProximityPos = spaceProximityPos; + mMissingSpacePos = missingSpacePos; +} + +void Correction::checkState() { + if (DEBUG_DICT) { + int inputCount = 0; + if (mSkipPos >= 0) ++inputCount; + if (mExcessivePos >= 0) ++inputCount; + if (mTransposedPos >= 0) ++inputCount; + // TODO: remove this assert + assert(inputCount <= 1); + } +} + +int Correction::getFreqForSplitTwoWords(const int firstFreq, const int secondFreq) { + return Correction::RankingAlgorithm::calcFreqForSplitTwoWords(firstFreq, secondFreq, this); +} + +int Correction::getFinalFreq(const int freq, unsigned short **word, int *wordLength) { + const int outputIndex = mTerminalOutputIndex; + const int inputIndex = mTerminalInputIndex; + *wordLength = outputIndex + 1; + if (mProximityInfo->sameAsTyped(mWord, outputIndex + 1) || outputIndex < MIN_SUGGEST_DEPTH) { + return -1; + } + + *word = mWord; + return Correction::RankingAlgorithm::calculateFinalFreq( + inputIndex, outputIndex, freq, mEditDistanceTable, this); +} + +bool Correction::initProcessState(const int outputIndex) { + if (mCorrectionStates[outputIndex].mChildCount <= 0) { + return false; + } + mOutputIndex = outputIndex; + --(mCorrectionStates[outputIndex].mChildCount); + mInputIndex = mCorrectionStates[outputIndex].mInputIndex; + mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes; + mProximityCount = mCorrectionStates[outputIndex].mProximityCount; + mSkippedCount = mCorrectionStates[outputIndex].mSkippedCount; + mSkipPos = mCorrectionStates[outputIndex].mSkipPos; + mSkipping = false; + mProximityMatching = false; + mMatching = false; + return true; +} + +int Correction::goDownTree( + const int parentIndex, const int childCount, const int firstChildPos) { + mCorrectionStates[mOutputIndex].mParentIndex = parentIndex; + mCorrectionStates[mOutputIndex].mChildCount = childCount; + mCorrectionStates[mOutputIndex].mSiblingPos = firstChildPos; + return mOutputIndex; +} + +// TODO: remove +int Correction::getOutputIndex() { + return mOutputIndex; +} + +// TODO: remove +int Correction::getInputIndex() { + return mInputIndex; +} + +// TODO: remove +bool Correction::needsToTraverseAllNodes() { + return mNeedsToTraverseAllNodes; +} + +void Correction::incrementInputIndex() { + ++mInputIndex; +} + +void Correction::incrementOutputIndex() { + ++mOutputIndex; + mCorrectionStates[mOutputIndex].mParentIndex = mCorrectionStates[mOutputIndex - 1].mParentIndex; + mCorrectionStates[mOutputIndex].mChildCount = mCorrectionStates[mOutputIndex - 1].mChildCount; + mCorrectionStates[mOutputIndex].mSiblingPos = mCorrectionStates[mOutputIndex - 1].mSiblingPos; + mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex; + mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes; + mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount; + mCorrectionStates[mOutputIndex].mSkippedCount = mSkippedCount; + mCorrectionStates[mOutputIndex].mSkipping = mSkipping; + mCorrectionStates[mOutputIndex].mSkipPos = mSkipPos; + mCorrectionStates[mOutputIndex].mMatching = mMatching; + mCorrectionStates[mOutputIndex].mProximityMatching = mProximityMatching; +} + +void Correction::startToTraverseAllNodes() { + mNeedsToTraverseAllNodes = true; +} + +bool Correction::needsToPrune() const { + return (mOutputIndex - 1 >= (mTransposedPos >= 0 ? mInputLength - 1 : mMaxDepth) + || mProximityCount > mMaxEditDistance); +} + +Correction::CorrectionType Correction::processSkipChar( + const int32_t c, const bool isTerminal) { + mWord[mOutputIndex] = c; + if (needsToTraverseAllNodes() && isTerminal) { + mTerminalInputIndex = mInputIndex; + mTerminalOutputIndex = mOutputIndex; + incrementOutputIndex(); + return TRAVERSE_ALL_ON_TERMINAL; + } else { + incrementOutputIndex(); + return TRAVERSE_ALL_NOT_ON_TERMINAL; + } +} + +Correction::CorrectionType Correction::processCharAndCalcState( + const int32_t c, const bool isTerminal) { + CorrectionType currentStateType = NOT_ON_TERMINAL; + // This has to be done for each virtual char (this forwards the "inputIndex" which + // is the index in the user-inputted chars, as read by proximity chars. + if (mExcessivePos == mOutputIndex && mInputIndex < mInputLength - 1) { + incrementInputIndex(); + } + + bool skip = false; + if (mSkipPos >= 0) { + if (mSkippedCount == 0 && mSkipPos < mOutputIndex) { + if (DEBUG_DICT) { + assert(mSkipPos == mOutputIndex - 1); + } + ++mSkipPos; + } + skip = mSkipPos == mOutputIndex; + mSkipping = true; + } + + if (mNeedsToTraverseAllNodes || isQuote(c)) { + return processSkipChar(c, isTerminal); + } else { + int inputIndexForProximity = mInputIndex; + + if (mTransposedPos >= 0) { + if (mInputIndex == mTransposedPos) { + ++inputIndexForProximity; + } + if (mInputIndex == (mTransposedPos + 1)) { + --inputIndexForProximity; + } + } + + // TODO: sum counters + const bool checkProximityChars = + !(mSkippedCount > 0 || mExcessivePos >= 0 || mTransposedPos >= 0); + int matchedProximityCharId = mProximityInfo->getMatchedProximityId( + inputIndexForProximity, c, checkProximityChars); + + if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) { + if (skip && mProximityCount == 0) { + // Skip this letter and continue deeper + ++mSkippedCount; + return processSkipChar(c, isTerminal); + } else if (checkProximityChars + && inputIndexForProximity > 0 + && mCorrectionStates[mOutputIndex].mProximityMatching + && mCorrectionStates[mOutputIndex].mSkipping + && mProximityInfo->getMatchedProximityId( + inputIndexForProximity - 1, c, false) + == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + // 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. + ++mSkippedCount; + --mProximityCount; + return processSkipChar(c, isTerminal); + } else { + return UNRELATED; + } + } else if (ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) { + // If inputIndex is greater than mInputLength, that means there is no + // proximity chars. So, we don't need to check proximity. + mMatching = true; + } else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) { + mProximityMatching = true; + incrementProximityCount(); + } + + mWord[mOutputIndex] = c; + + const bool isSameAsUserTypedLength = mInputLength + == getInputIndex() + 1 + || (mExcessivePos == mInputLength - 1 + && getInputIndex() == mInputLength - 2); + if (isSameAsUserTypedLength && isTerminal) { + mTerminalInputIndex = mInputIndex; + mTerminalOutputIndex = mOutputIndex; + currentStateType = ON_TERMINAL; + } + // Start traversing all nodes after the index exceeds the user typed length + if (isSameAsUserTypedLength) { + startToTraverseAllNodes(); + } + + // Finally, we are ready to go to the next character, the next "virtual node". + // We should advance the input index. + // We do this in this branch of the 'if traverseAllNodes' because we are still matching + // characters to input; the other branch is not matching them but searching for + // completions, this is why it does not have to do it. + incrementInputIndex(); + } + + // Also, the next char is one "virtual node" depth more than this char. + incrementOutputIndex(); + + return currentStateType; +} + +Correction::~Correction() { +} + +///////////////////////// +// static inline utils // +///////////////////////// + +static const int TWO_31ST_DIV_255 = S_INT_MAX / 255; +static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) { + return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX); +} + +static const int TWO_31ST_DIV_2 = S_INT_MAX / 2; +inline static void multiplyIntCapped(const int multiplier, int *base) { + const int temp = *base; + if (temp != S_INT_MAX) { + // Branch if multiplier == 2 for the optimization + if (multiplier == 2) { + *base = TWO_31ST_DIV_2 >= temp ? temp << 1 : S_INT_MAX; + } else { + const int tempRetval = temp * multiplier; + *base = tempRetval >= temp ? tempRetval : S_INT_MAX; + } + } +} + +inline static int powerIntCapped(const int base, const int n) { + if (n == 0) return 1; + if (base == 2) { + return n < 31 ? 1 << n : S_INT_MAX; + } else { + int ret = base; + for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret); + return ret; + } +} + +inline static void multiplyRate(const int rate, int *freq) { + if (*freq != S_INT_MAX) { + if (*freq > 1000000) { + *freq /= 100; + multiplyIntCapped(rate, freq); + } else { + multiplyIntCapped(rate, freq); + *freq /= 100; + } + } +} + +inline static int getQuoteCount(const unsigned short* word, const int length) { + int quoteCount = 0; + for (int i = 0; i < length; ++i) { + if(word[i] == '\'') { + ++quoteCount; + } + } + return quoteCount; +} + +/* static */ +inline static int editDistance( + int* editDistanceTable, const unsigned short* input, + const int inputLength, const unsigned short* output, const int outputLength) { + // dp[li][lo] dp[a][b] = dp[ a * lo + b] + int* dp = editDistanceTable; + const int li = inputLength + 1; + const int lo = outputLength + 1; + for (int i = 0; i < li; ++i) { + dp[lo * i] = i; + } + for (int i = 0; i < lo; ++i) { + dp[i] = i; + } + + for (int i = 0; i < li - 1; ++i) { + for (int j = 0; j < lo - 1; ++j) { + const uint32_t ci = Dictionary::toBaseLowerCase(input[i]); + const uint32_t co = Dictionary::toBaseLowerCase(output[j]); + const uint16_t cost = (ci == co) ? 0 : 1; + dp[(i + 1) * lo + (j + 1)] = min(dp[i * lo + (j + 1)] + 1, + min(dp[(i + 1) * lo + j] + 1, dp[i * lo + j] + cost)); + if (li > 0 && lo > 0 + && ci == Dictionary::toBaseLowerCase(output[j - 1]) + && co == Dictionary::toBaseLowerCase(input[i - 1])) { + dp[(i + 1) * lo + (j + 1)] = min( + dp[(i + 1) * lo + (j + 1)], dp[(i - 1) * lo + (j - 1)] + cost); + } + } + } + + if (DEBUG_EDIT_DISTANCE) { + LOGI("IN = %d, OUT = %d", inputLength, outputLength); + for (int i = 0; i < li; ++i) { + for (int j = 0; j < lo; ++j) { + LOGI("EDIT[%d][%d], %d", i, j, dp[i * lo + j]); + } + } + } + return dp[li * lo - 1]; +} + +////////////////////// +// RankingAlgorithm // +////////////////////// + +/* static */ +int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const int outputIndex, + const int freq, int* editDistanceTable, const Correction* correction) { + const int excessivePos = correction->getExcessivePos(); + const int transposedPos = correction->getTransposedPos(); + const int inputLength = correction->mInputLength; + const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER; + const int fullWordMultiplier = correction->FULL_WORD_MULTIPLIER; + const ProximityInfo *proximityInfo = correction->mProximityInfo; + const int skipCount = correction->mSkippedCount; + const int proximityMatchedCount = correction->mProximityCount; + if (skipCount >= inputLength || inputLength == 0) { + return -1; + } + const bool sameLength = (excessivePos == inputLength - 1) ? (inputLength == inputIndex + 2) + : (inputLength == inputIndex + 1); + + + // TODO: use mExcessiveCount + int matchCount = inputLength - correction->mProximityCount - (excessivePos >= 0 ? 1 : 0); + + const unsigned short* word = correction->mWord; + const bool skipped = skipCount > 0; + + const int quoteDiffCount = max(0, getQuoteCount(word, outputIndex + 1) + - getQuoteCount(proximityInfo->getPrimaryInputWord(), inputLength)); + + // TODO: Calculate edit distance for transposed and excessive + int matchWeight; + int ed = 0; + int adJustedProximityMatchedCount = proximityMatchedCount; + if (excessivePos < 0 && transposedPos < 0 && (proximityMatchedCount > 0 || skipped)) { + const unsigned short* primaryInputWord = proximityInfo->getPrimaryInputWord(); + ed = editDistance(editDistanceTable, primaryInputWord, + inputLength, word, outputIndex + 1); + matchWeight = powerIntCapped(typedLetterMultiplier, outputIndex + 1 - ed); + if (ed == 1 && inputLength == outputIndex) { + // Promote a word with just one skipped char + multiplyRate(WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE, &matchWeight); + } + ed = max(0, ed - quoteDiffCount); + adJustedProximityMatchedCount = min(max(0, ed - (outputIndex + 1 - inputLength)), + proximityMatchedCount); + } else { + matchWeight = powerIntCapped(typedLetterMultiplier, matchCount); + } + + // TODO: Demote by edit distance + int finalFreq = freq * matchWeight; + + /////////////////////////////////////////////// + // Promotion and Demotion for each correction + + // Demotion for a word with missing character + if (skipped) { + const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE + * (10 * inputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X) + / (10 * inputLength + - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10); + if (DEBUG_DICT_FULL) { + LOGI("Demotion rate for missing character is %d.", demotionRate); + } + multiplyRate(demotionRate, &finalFreq); + } + + // Demotion for a word with transposed character + if (transposedPos >= 0) multiplyRate( + WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq); + + // Demotion for a word with excessive character + if (excessivePos >= 0) { + multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq); + if (!proximityInfo->existsAdjacentProximityChars(inputIndex)) { + // If an excessive character is not adjacent to the left char or the right char, + // we will demote this word. + multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE, &finalFreq); + } + } + + // Promotion for a word with proximity characters + for (int i = 0; i < adJustedProximityMatchedCount; ++i) { + // A word with proximity corrections + if (DEBUG_DICT_FULL) { + LOGI("Found a proximity correction."); + } + multiplyIntCapped(typedLetterMultiplier, &finalFreq); + multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq); + } + + const int errorCount = proximityMatchedCount + skipCount; + multiplyRate( + 100 - CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE * errorCount / inputLength, &finalFreq); + + // Promotion for an exactly matched word + if (matchCount == outputIndex + 1) { + // Full exact match + if (sameLength && transposedPos < 0 && !skipped && excessivePos < 0) { + finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq); + } + } + + // Promote a word with no correction + if (proximityMatchedCount == 0 && transposedPos < 0 && !skipped && excessivePos < 0) { + multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq); + } + + // TODO: Check excessive count and transposed count + // TODO: Remove this if possible + /* + If the last character of the user input word is the same as the next character + of the output word, and also all of characters of the user input are matched + to the output word, we'll promote that word a bit because + that word can be considered the combination of skipped and matched characters. + This means that the 'sm' pattern wins over the 'ma' pattern. + e.g.) + shel -> shell [mmmma] or [mmmsm] + hel -> hello [mmmaa] or [mmsma] + m ... matching + s ... skipping + a ... traversing all + */ + if (matchCount == inputLength && matchCount >= 2 && !skipped + && word[matchCount] == word[matchCount - 1]) { + multiplyRate(WORDS_WITH_MATCH_SKIP_PROMOTION_RATE, &finalFreq); + } + + if (sameLength) { + multiplyIntCapped(fullWordMultiplier, &finalFreq); + } + + if (DEBUG_DICT_FULL) { + LOGI("calc: %d, %d", outputIndex, sameLength); + } + + return finalFreq; +} + +/* static */ +int Correction::RankingAlgorithm::calcFreqForSplitTwoWords( + const int firstFreq, const int secondFreq, const Correction* correction) { + const int spaceProximityPos = correction->mSpaceProximityPos; + const int missingSpacePos = correction->mMissingSpacePos; + if (DEBUG_DICT) { + int inputCount = 0; + if (spaceProximityPos >= 0) ++inputCount; + if (missingSpacePos >= 0) ++inputCount; + assert(inputCount <= 1); + } + const bool isSpaceProximity = spaceProximityPos >= 0; + const int inputLength = correction->mInputLength; + const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos; + const int secondWordLength = isSpaceProximity + ? (inputLength - spaceProximityPos - 1) + : (inputLength - missingSpacePos); + const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER; + + if (firstWordLength == 0 || secondWordLength == 0) { + return 0; + } + const int firstDemotionRate = 100 - 100 / (firstWordLength + 1); + int tempFirstFreq = firstFreq; + multiplyRate(firstDemotionRate, &tempFirstFreq); + + const int secondDemotionRate = 100 - 100 / (secondWordLength + 1); + int tempSecondFreq = secondFreq; + multiplyRate(secondDemotionRate, &tempSecondFreq); + + const int totalLength = firstWordLength + secondWordLength; + + // Promote pairFreq with multiplying by 2, because the word length is the same as the typed + // length. + int totalFreq = tempFirstFreq + tempSecondFreq; + + // This is a workaround to try offsetting the not-enough-demotion which will be done in + // calcNormalizedScore in Utils.java. + // In calcNormalizedScore the score will be demoted by (1 - 1 / length) + // but we demoted only (1 - 1 / (length + 1)) so we will additionally adjust freq by + // (1 - 1 / length) / (1 - 1 / (length + 1)) = (1 - 1 / (length * length)) + const int normalizedScoreNotEnoughDemotionAdjustment = 100 - 100 / (totalLength * totalLength); + multiplyRate(normalizedScoreNotEnoughDemotionAdjustment, &totalFreq); + + // At this moment, totalFreq is calculated by the following formula: + // (firstFreq * (1 - 1 / (firstWordLength + 1)) + secondFreq * (1 - 1 / (secondWordLength + 1))) + // * (1 - 1 / totalLength) / (1 - 1 / (totalLength + 1)) + + multiplyIntCapped(powerIntCapped(typedLetterMultiplier, totalLength), &totalFreq); + + // This is another workaround to offset the demotion which will be done in + // calcNormalizedScore in Utils.java. + // In calcNormalizedScore the score will be demoted by (1 - 1 / length) so we have to promote + // the same amount because we already have adjusted the synthetic freq of this "missing or + // mistyped space" suggestion candidate above in this method. + const int normalizedScoreDemotionRateOffset = (100 + 100 / totalLength); + multiplyRate(normalizedScoreDemotionRateOffset, &totalFreq); + + if (isSpaceProximity) { + // A word pair with one space proximity correction + if (DEBUG_DICT) { + LOGI("Found a word pair with space proximity correction."); + } + multiplyIntCapped(typedLetterMultiplier, &totalFreq); + multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &totalFreq); + } + + multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &totalFreq); + return totalFreq; +} + +} // namespace latinime diff --git a/native/src/correction.h b/native/src/correction.h new file mode 100644 index 000000000..871a04251 --- /dev/null +++ b/native/src/correction.h @@ -0,0 +1,148 @@ +/* + * 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. + */ + +#ifndef LATINIME_CORRECTION_H +#define LATINIME_CORRECTION_H + +#include <stdint.h> +#include "correction_state.h" + +#include "defines.h" + +namespace latinime { + +class ProximityInfo; + +class Correction { + +public: + typedef enum { + TRAVERSE_ALL_ON_TERMINAL, + TRAVERSE_ALL_NOT_ON_TERMINAL, + UNRELATED, + ON_TERMINAL, + NOT_ON_TERMINAL + } CorrectionType; + + Correction(const int typedLetterMultiplier, const int fullWordMultiplier); + void initCorrection( + const ProximityInfo *pi, const int inputLength, const int maxWordLength); + void initCorrectionState(const int rootPos, const int childCount, const bool traverseAll); + + // TODO: remove + void setCorrectionParams(const int skipPos, const int excessivePos, const int transposedPos, + const int spaceProximityPos, const int missingSpacePos); + void checkState(); + bool initProcessState(const int index); + + int getOutputIndex(); + int getInputIndex(); + + virtual ~Correction(); + int getSpaceProximityPos() const { + return mSpaceProximityPos; + } + int getMissingSpacePos() const { + return mMissingSpacePos; + } + + int getSkipPos() const { + return mSkipPos; + } + + int getExcessivePos() const { + return mExcessivePos; + } + + int getTransposedPos() const { + return mTransposedPos; + } + + bool needsToPrune() const; + + int getFreqForSplitTwoWords(const int firstFreq, const int secondFreq); + int getFinalFreq(const int freq, unsigned short **word, int* wordLength); + + CorrectionType processCharAndCalcState(const int32_t c, const bool isTerminal); + + ///////////////////////// + // Tree helper methods + int goDownTree(const int parentIndex, const int childCount, const int firstChildPos); + + inline int getTreeSiblingPos(const int index) const { + return mCorrectionStates[index].mSiblingPos; + } + + inline void setTreeSiblingPos(const int index, const int pos) { + mCorrectionStates[index].mSiblingPos = pos; + } + + inline int getTreeParentIndex(const int index) const { + return mCorrectionStates[index].mParentIndex; + } +private: + inline void incrementInputIndex(); + inline void incrementOutputIndex(); + inline bool needsToTraverseAllNodes(); + inline void startToTraverseAllNodes(); + inline bool isQuote(const unsigned short c); + inline CorrectionType processSkipChar(const int32_t c, const bool isTerminal); + + // TODO: remove + inline void incrementProximityCount() { + ++mProximityCount; + } + + const int TYPED_LETTER_MULTIPLIER; + const int FULL_WORD_MULTIPLIER; + const ProximityInfo *mProximityInfo; + + int mMaxEditDistance; + int mMaxDepth; + int mInputLength; + int mExcessivePos; + int mTransposedPos; + int mSpaceProximityPos; + int mMissingSpacePos; + int mTerminalInputIndex; + int mTerminalOutputIndex; + unsigned short mWord[MAX_WORD_LENGTH_INTERNAL]; + // Caveat: Do not create multiple tables per thread as this table eats up RAM a lot. + int mEditDistanceTable[MAX_WORD_LENGTH_INTERNAL * MAX_WORD_LENGTH_INTERNAL]; + + CorrectionState mCorrectionStates[MAX_WORD_LENGTH_INTERNAL]; + + // The following member variables are being used as cache values of the correction state. + int mOutputIndex; + int mInputIndex; + int mProximityCount; + int mSkippedCount; + int mSkipPos; + bool mNeedsToTraverseAllNodes; + bool mMatching; + bool mSkipping; + bool mProximityMatching; + + class RankingAlgorithm { + public: + static int calculateFinalFreq(const int inputIndex, const int depth, + const int freq, int *editDistanceTable, const Correction* correction); + static int calcFreqForSplitTwoWords(const int firstFreq, const int secondFreq, + const Correction* correction); + }; +}; +} // namespace latinime +#endif // LATINIME_CORRECTION_H diff --git a/native/src/correction_state.h b/native/src/correction_state.h new file mode 100644 index 000000000..267deda9b --- /dev/null +++ b/native/src/correction_state.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef LATINIME_CORRECTION_STATE_H +#define LATINIME_CORRECTION_STATE_H + +#include <stdint.h> + +#include "defines.h" + +namespace latinime { + +struct CorrectionState { + int mParentIndex; + int mSiblingPos; + uint16_t mChildCount; + uint8_t mInputIndex; + uint8_t mProximityCount; + uint8_t mSkippedCount; + int8_t mSkipPos; // should be signed + bool mMatching; + bool mSkipping; + bool mProximityMatching; + bool mNeedsToTraverseAllNodes; + +}; + +inline static void initCorrectionState(CorrectionState *state, const int rootPos, + const uint16_t childCount, const bool traverseAll) { + state->mParentIndex = -1; + state->mChildCount = childCount; + state->mInputIndex = 0; + state->mProximityCount = 0; + state->mSiblingPos = rootPos; + state->mSkippedCount = 0; + state->mMatching = false; + state->mSkipping = false; + state->mProximityMatching = false; + state->mNeedsToTraverseAllNodes = traverseAll; + state->mSkipPos = -1; +} + +} // namespace latinime +#endif // LATINIME_CORRECTION_STATE_H diff --git a/native/src/defines.h b/native/src/defines.h index bea83b2c5..a29fb7e5b 100644 --- a/native/src/defines.h +++ b/native/src/defines.h @@ -94,20 +94,36 @@ static void prof_out(void) { #endif #define DEBUG_DICT true #define DEBUG_DICT_FULL false +#define DEBUG_EDIT_DISTANCE false #define DEBUG_SHOW_FOUND_WORD DEBUG_DICT_FULL #define DEBUG_NODE DEBUG_DICT_FULL #define DEBUG_TRACE DEBUG_DICT_FULL #define DEBUG_PROXIMITY_INFO true +#define DUMP_WORD(word, length) do { dumpWord(word, length); } while(0) + +static char charBuf[50]; + +static void dumpWord(const unsigned short* word, const int length) { + for (int i = 0; i < length; ++i) { + charBuf[i] = word[i]; + } + charBuf[length] = 0; + LOGI("[ %s ]", charBuf); +} + #else // FLAG_DBG #define DEBUG_DICT false #define DEBUG_DICT_FULL false +#define DEBUG_EDIT_DISTANCE false #define DEBUG_SHOW_FOUND_WORD false #define DEBUG_NODE false #define DEBUG_TRACE false #define DEBUG_PROXIMITY_INFO false +#define DUMP_WORD(word, length) + #endif // FLAG_DBG #ifndef U_SHORT_MAX @@ -160,6 +176,9 @@ static void prof_out(void) { #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120 #define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90 +#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105 +#define WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE 160 +#define CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE 42 // This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java // This is only used for the size of array. Not to be used in c functions. @@ -176,9 +195,7 @@ static void prof_out(void) { #define MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION 3 #define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3 -// The size of next letters frequency array. Zero will disable the feature. -#define NEXT_LETTERS_SIZE 0 - #define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) #endif // LATINIME_DEFINES_H diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp index 9e32ee80f..a49769bdb 100644 --- a/native/src/dictionary.cpp +++ b/native/src/dictionary.cpp @@ -57,12 +57,4 @@ bool Dictionary::isValidWord(unsigned short *word, int length) { return mUnigramDictionary->isValidWord(word, length); } -int Dictionary::getBigramPosition(unsigned short *word, int length) { - if (IS_LATEST_DICT_VERSION) { - return mUnigramDictionary->getBigramPosition(DICTIONARY_HEADER_SIZE, word, 0, length); - } else { - return mUnigramDictionary->getBigramPosition(0, word, 0, length); - } -} - } // namespace latinime diff --git a/native/src/dictionary.h b/native/src/dictionary.h index 73e03d8fd..d5de0083a 100644 --- a/native/src/dictionary.h +++ b/native/src/dictionary.h @@ -64,8 +64,6 @@ public: const int pos, unsigned short *c, int *childrenPosition, bool *terminal, int *freq); static inline unsigned short toBaseLowerCase(unsigned short c); - // TODO: delete this - int getBigramPosition(unsigned short *word, int length); private: bool hasBigram(); diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp index c45393f18..361bdacbf 100644 --- a/native/src/proximity_info.cpp +++ b/native/src/proximity_info.cpp @@ -68,6 +68,10 @@ bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength) { mInputCodes = inputCodes; mInputLength = inputLength; + for (int i = 0; i < inputLength; ++i) { + mPrimaryInputWord[i] = getPrimaryCharAt(i); + } + mPrimaryInputWord[inputLength] = 0; } inline const int* ProximityInfo::getProximityCharsAt(const int index) const { @@ -78,7 +82,7 @@ unsigned short ProximityInfo::getPrimaryCharAt(const int index) const { return getProximityCharsAt(index)[0]; } -bool ProximityInfo::existsCharInProximityAt(const int index, const int c) const { +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) { @@ -114,8 +118,7 @@ bool ProximityInfo::existsAdjacentProximityChars(const int index) const { // 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 int skipPos, - const int excessivePos, const int transposedPos) const { + const int index, const unsigned short c, const bool checkProximityChars) const { const int *currentChars = getProximityCharsAt(index); const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c); @@ -124,9 +127,7 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId( if (currentChars[0] == baseLowerC || currentChars[0] == c) return SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR; - // If one of those is true, we should not check for close characters at all. - if (skipPos >= 0 || excessivePos >= 0 || transposedPos >= 0) - return UNRELATED_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 diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h index 435a60151..75fc8fb63 100644 --- a/native/src/proximity_info.h +++ b/native/src/proximity_info.h @@ -23,6 +23,8 @@ namespace latinime { +class Correction; + class ProximityInfo { public: typedef enum { // Used as a return value for character comparison @@ -42,9 +44,12 @@ public: 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 int skipPos, - const int excessivePos, const int transposedPos) const; + const int index, const unsigned short c, const bool checkProximityChars) const; bool sameAsTyped(const unsigned short *word, int length) const; + const unsigned short* getPrimaryInputWord() const { + return mPrimaryInputWord; + } + private: int getStartIndexFromCoordinates(const int x, const int y) const; const int MAX_PROXIMITY_CHARS_SIZE; @@ -57,6 +62,7 @@ private: const int *mInputCodes; uint32_t *mProximityCharsArray; int mInputLength; + unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL]; }; } // namespace latinime diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index bccd37a61..6bc350505 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -24,9 +24,7 @@ #include "dictionary.h" #include "unigram_dictionary.h" -#ifdef NEW_DICTIONARY_FORMAT #include "binary_format.h" -#endif // NEW_DICTIONARY_FORMAT namespace latinime { @@ -39,28 +37,23 @@ const UnigramDictionary::digraph_t UnigramDictionary::GERMAN_UMLAUT_DIGRAPHS[] = UnigramDictionary::UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion) -#ifndef NEW_DICTIONARY_FORMAT - : DICT_ROOT(streamStart), -#else // NEW_DICTIONARY_FORMAT : DICT_ROOT(streamStart + NEW_DICTIONARY_HEADER_SIZE), -#endif // NEW_DICTIONARY_FORMAT MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords), MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion), TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier), -#ifndef NEW_DICTIONARY_FORMAT - ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0), -#else // NEW_DICTIONARY_FORMAT // TODO : remove this variable. ROOT_POS(0), -#endif // NEW_DICTIONARY_FORMAT BYTES_IN_ONE_CHAR(MAX_PROXIMITY_CHARS * sizeof(int)), MAX_UMLAUT_SEARCH_DEPTH(DEFAULT_MAX_UMLAUT_SEARCH_DEPTH) { if (DEBUG_DICT) { LOGI("UnigramDictionary - constructor"); } + mCorrection = new Correction(typedLetterMultiplier, fullWordMultiplier); } -UnigramDictionary::~UnigramDictionary() {} +UnigramDictionary::~UnigramDictionary() { + delete mCorrection; +} static inline unsigned int getCodesBufferSize(const int* codes, const int codesSize, const int MAX_PROXIMITY_CHARS) { @@ -174,12 +167,6 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x LOGI("%s %i", s, mFrequencies[j]); #endif } - LOGI("Next letters: "); - for (int k = 0; k < NEXT_LETTERS_SIZE; k++) { - if (mNextLettersFrequency[k] > 0) { - LOGI("%c = %d,", k, mNextLettersFrequency[k]); - } - } } PROF_END(20); PROF_CLOSE; @@ -196,23 +183,19 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, proximityInfo, xcoordinates, ycoordinates, codes, codesSize, outWords, frequencies); if (DEBUG_DICT) assert(codesSize == mInputLength); - const int MAX_DEPTH = min(mInputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH); + const int maxDepth = min(mInputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH); + mCorrection->initCorrection(mProximityInfo, mInputLength, maxDepth); PROF_END(0); + // TODO: remove PROF_START(1); - getSuggestionCandidates(-1, -1, -1, mNextLettersFrequency, NEXT_LETTERS_SIZE, MAX_DEPTH); + // Note: This line is intentionally left blank PROF_END(1); PROF_START(2); // Suggestion with missing character - if (SUGGEST_WORDS_WITH_MISSING_CHARACTER) { - for (int i = 0; i < codesSize; ++i) { - if (DEBUG_DICT) { - LOGI("--- Suggest missing characters %d", i); - } - getSuggestionCandidates(i, -1, -1, NULL, 0, MAX_DEPTH); - } - } + LOGI("--- Suggest missing characters"); + getSuggestionCandidates(0, -1, -1); PROF_END(2); PROF_START(3); @@ -223,7 +206,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, if (DEBUG_DICT) { LOGI("--- Suggest excessive characters %d", i); } - getSuggestionCandidates(-1, i, -1, NULL, 0, MAX_DEPTH); + getSuggestionCandidates(-1, i, -1); } } PROF_END(3); @@ -236,7 +219,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, if (DEBUG_DICT) { LOGI("--- Suggest transposed characters %d", i); } - getSuggestionCandidates(-1, -1, i, NULL, 0, mInputLength - 1); + getSuggestionCandidates(-1, -1, i); } } PROF_END(4); @@ -249,7 +232,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, if (DEBUG_DICT) { LOGI("--- Suggest missing space characters %d", i); } - getMissingSpaceWords(mInputLength, i); + getMissingSpaceWords(mInputLength, i, mCorrection); } } PROF_END(5); @@ -268,7 +251,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, i, x, y, proximityInfo->hasSpaceProximity(x, y)); } if (proximityInfo->hasSpaceProximity(x, y)) { - getMistypedSpaceWords(mInputLength, i); + getMistypedSpaceWords(mInputLength, i, mCorrection); } } } @@ -284,7 +267,6 @@ void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int mFrequencies = frequencies; mOutputChars = outWords; mInputLength = codesSize; - mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2; proximityInfo->setInputParams(codes, codesSize); mProximityInfo = proximityInfo; } @@ -354,70 +336,43 @@ static const char QUOTE = '\''; static const char SPACE = ' '; void UnigramDictionary::getSuggestionCandidates(const int skipPos, - const int excessivePos, const int transposedPos, int *nextLetters, - const int nextLettersSize, const int maxDepth) { + const int excessivePos, const int transposedPos) { if (DEBUG_DICT) { - LOGI("getSuggestionCandidates %d", maxDepth); assert(transposedPos + 1 < mInputLength); assert(excessivePos < mInputLength); assert(missingPos < mInputLength); } + mCorrection->setCorrectionParams(skipPos, excessivePos, transposedPos, + -1 /* spaceProximityPos */, -1 /* missingSpacePos */); int rootPosition = ROOT_POS; // Get the number of children of root, then increment the position int childCount = Dictionary::getCount(DICT_ROOT, &rootPosition); - int depth = 0; + int outputIndex = 0; - mStackChildCount[0] = childCount; - mStackTraverseAll[0] = (mInputLength <= 0); - mStackNodeFreq[0] = 1; - mStackInputIndex[0] = 0; - mStackDiffs[0] = 0; - mStackSiblingPos[0] = rootPosition; - mStackOutputIndex[0] = 0; + mCorrection->initCorrectionState(rootPosition, childCount, (mInputLength <= 0)); // Depth first search - while (depth >= 0) { - if (mStackChildCount[depth] > 0) { - --mStackChildCount[depth]; - bool traverseAllNodes = mStackTraverseAll[depth]; - int matchWeight = mStackNodeFreq[depth]; - int inputIndex = mStackInputIndex[depth]; - int diffs = mStackDiffs[depth]; - int siblingPos = mStackSiblingPos[depth]; - int outputIndex = mStackOutputIndex[depth]; + while (outputIndex >= 0) { + if (mCorrection->initProcessState(outputIndex)) { + int siblingPos = mCorrection->getTreeSiblingPos(outputIndex); int firstChildPos; - // depth will never be greater than maxDepth because in that case, - // needsToTraverseChildrenNodes should be false - const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, outputIndex, - maxDepth, traverseAllNodes, matchWeight, inputIndex, diffs, skipPos, - excessivePos, transposedPos, nextLetters, nextLettersSize, &childCount, - &firstChildPos, &traverseAllNodes, &matchWeight, &inputIndex, &diffs, - &siblingPos, &outputIndex); + + const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, + mCorrection, &childCount, &firstChildPos, &siblingPos); // Update next sibling pos - mStackSiblingPos[depth] = siblingPos; + mCorrection->setTreeSiblingPos(outputIndex, siblingPos); + if (needsToTraverseChildrenNodes) { // Goes to child node - ++depth; - mStackChildCount[depth] = childCount; - mStackTraverseAll[depth] = traverseAllNodes; - mStackNodeFreq[depth] = matchWeight; - mStackInputIndex[depth] = inputIndex; - mStackDiffs[depth] = diffs; - mStackSiblingPos[depth] = firstChildPos; - mStackOutputIndex[depth] = outputIndex; + outputIndex = mCorrection->goDownTree(outputIndex, childCount, firstChildPos); } } else { // Goes to parent sibling node - --depth; + outputIndex = mCorrection->getTreeParentIndex(outputIndex); } } } -static const int TWO_31ST_DIV_255 = S_INT_MAX / 255; -static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) { - return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX); -} - static const int TWO_31ST_DIV_2 = S_INT_MAX / 2; inline static void multiplyIntCapped(const int multiplier, int *base) { const int temp = *base; @@ -432,149 +387,18 @@ inline static void multiplyIntCapped(const int multiplier, int *base) { } } -inline static int powerIntCapped(const int base, const int n) { - if (base == 2) { - return n < 31 ? 1 << n : S_INT_MAX; - } else { - int ret = base; - for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret); - return ret; - } +void UnigramDictionary::getMissingSpaceWords( + const int inputLength, const int missingSpacePos, Correction *correction) { + correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */, + -1 /* transposedPos */, -1 /* spaceProximityPos */, missingSpacePos); + getSplitTwoWordsSuggestion(inputLength, correction); } -inline static void multiplyRate(const int rate, int *freq) { - if (*freq != S_INT_MAX) { - if (*freq > 1000000) { - *freq /= 100; - multiplyIntCapped(rate, freq); - } else { - multiplyIntCapped(rate, freq); - *freq /= 100; - } - } -} - -inline static int calcFreqForSplitTwoWords( - const int typedLetterMultiplier, const int firstWordLength, const int secondWordLength, - const int firstFreq, const int secondFreq, const bool isSpaceProximity) { - if (firstWordLength == 0 || secondWordLength == 0) { - return 0; - } - const int firstDemotionRate = 100 - 100 / (firstWordLength + 1); - int tempFirstFreq = firstFreq; - multiplyRate(firstDemotionRate, &tempFirstFreq); - - const int secondDemotionRate = 100 - 100 / (secondWordLength + 1); - int tempSecondFreq = secondFreq; - multiplyRate(secondDemotionRate, &tempSecondFreq); - - const int totalLength = firstWordLength + secondWordLength; - - // Promote pairFreq with multiplying by 2, because the word length is the same as the typed - // length. - int totalFreq = tempFirstFreq + tempSecondFreq; - - // This is a workaround to try offsetting the not-enough-demotion which will be done in - // calcNormalizedScore in Utils.java. - // In calcNormalizedScore the score will be demoted by (1 - 1 / length) - // but we demoted only (1 - 1 / (length + 1)) so we will additionally adjust freq by - // (1 - 1 / length) / (1 - 1 / (length + 1)) = (1 - 1 / (length * length)) - const int normalizedScoreNotEnoughDemotionAdjustment = 100 - 100 / (totalLength * totalLength); - multiplyRate(normalizedScoreNotEnoughDemotionAdjustment, &totalFreq); - - // At this moment, totalFreq is calculated by the following formula: - // (firstFreq * (1 - 1 / (firstWordLength + 1)) + secondFreq * (1 - 1 / (secondWordLength + 1))) - // * (1 - 1 / totalLength) / (1 - 1 / (totalLength + 1)) - - multiplyIntCapped(powerIntCapped(typedLetterMultiplier, totalLength), &totalFreq); - - // This is another workaround to offset the demotion which will be done in - // calcNormalizedScore in Utils.java. - // In calcNormalizedScore the score will be demoted by (1 - 1 / length) so we have to promote - // the same amount because we already have adjusted the synthetic freq of this "missing or - // mistyped space" suggestion candidate above in this method. - const int normalizedScoreDemotionRateOffset = (100 + 100 / totalLength); - multiplyRate(normalizedScoreDemotionRateOffset, &totalFreq); - - if (isSpaceProximity) { - // A word pair with one space proximity correction - if (DEBUG_DICT) { - LOGI("Found a word pair with space proximity correction."); - } - multiplyIntCapped(typedLetterMultiplier, &totalFreq); - multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &totalFreq); - } - - multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &totalFreq); - return totalFreq; -} - -bool UnigramDictionary::getMissingSpaceWords(const int inputLength, const int missingSpacePos) { - return getSplitTwoWordsSuggestion( - inputLength, 0, missingSpacePos, missingSpacePos, inputLength - missingSpacePos, false); -} - -bool UnigramDictionary::getMistypedSpaceWords(const int inputLength, const int spaceProximityPos) { - return getSplitTwoWordsSuggestion( - inputLength, 0, spaceProximityPos, spaceProximityPos + 1, - inputLength - spaceProximityPos - 1, true); -} - -inline int UnigramDictionary::calculateFinalFreq(const int inputIndex, const int depth, - const int matchWeight, const int skipPos, const int excessivePos, const int transposedPos, - const int freq, const bool sameLength) const { - // TODO: Demote by edit distance - int finalFreq = freq * matchWeight; - if (skipPos >= 0) { - if (mInputLength >= 2) { - const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE - * (10 * mInputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X) - / (10 * mInputLength - - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10); - if (DEBUG_DICT_FULL) { - LOGI("Demotion rate for missing character is %d.", demotionRate); - } - multiplyRate(demotionRate, &finalFreq); - } else { - finalFreq = 0; - } - } - if (transposedPos >= 0) multiplyRate( - WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq); - if (excessivePos >= 0) { - multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq); - if (!mProximityInfo->existsAdjacentProximityChars(inputIndex)) { - // If an excessive character is not adjacent to the left char or the right char, - // we will demote this word. - multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE, &finalFreq); - } - } - int lengthFreq = TYPED_LETTER_MULTIPLIER; - multiplyIntCapped(powerIntCapped(TYPED_LETTER_MULTIPLIER, depth), &lengthFreq); - if (lengthFreq == matchWeight) { - // Full exact match - if (depth > 1) { - if (DEBUG_DICT) { - LOGI("Found full matched word."); - } - multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq); - } - if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0) { - finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq); - } - } else if (sameLength && transposedPos < 0 && skipPos < 0 && excessivePos < 0 && depth > 0) { - // A word with proximity corrections - if (DEBUG_DICT) { - LOGI("Found one proximity correction."); - } - multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &finalFreq); - multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq); - } - if (DEBUG_DICT) { - LOGI("calc: %d, %d", depth, sameLength); - } - if (sameLength) multiplyIntCapped(FULL_WORD_MULTIPLIER, &finalFreq); - return finalFreq; +void UnigramDictionary::getMistypedSpaceWords( + const int inputLength, const int spaceProximityPos, Correction *correction) { + correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */, + -1 /* transposedPos */, spaceProximityPos, -1 /* missingSpacePos */); + getSplitTwoWordsSuggestion(inputLength, correction); } inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c, @@ -584,35 +408,38 @@ inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c, return (c == QUOTE && userTypedChar != QUOTE) || skipPos == depth; } - -inline void UnigramDictionary::onTerminal(unsigned short int* word, const int depth, - const uint8_t* const root, const uint8_t flags, const int pos, - const int inputIndex, const int matchWeight, const int skipPos, - const int excessivePos, const int transposedPos, const int freq, const bool sameLength, - int* nextLetters, const int nextLettersSize) { - - const bool isSameAsTyped = sameLength ? mProximityInfo->sameAsTyped(word, depth + 1) : false; - if (isSameAsTyped) return; - - if (depth >= MIN_SUGGEST_DEPTH) { - const int finalFreq = calculateFinalFreq(inputIndex, depth, matchWeight, skipPos, - excessivePos, transposedPos, freq, sameLength); - if (!isSameAsTyped) - addWord(word, depth + 1, finalFreq); +inline void UnigramDictionary::onTerminal(const int freq, Correction *correction) { + int wordLength; + unsigned short* wordPointer; + const int finalFreq = correction->getFinalFreq(freq, &wordPointer, &wordLength); + if (finalFreq >= 0) { + addWord(wordPointer, wordLength, finalFreq); } +} - if (sameLength && depth >= mInputLength && skipPos < 0) { - registerNextLetter(word[mInputLength], nextLetters, nextLettersSize); +void UnigramDictionary::getSplitTwoWordsSuggestion( + const int inputLength, Correction* correction) { + const int spaceProximityPos = correction->getSpaceProximityPos(); + const int missingSpacePos = correction->getMissingSpacePos(); + if (DEBUG_DICT) { + int inputCount = 0; + if (spaceProximityPos >= 0) ++inputCount; + if (missingSpacePos >= 0) ++inputCount; + assert(inputCount <= 1); } -} + const bool isSpaceProximity = spaceProximityPos >= 0; + const int firstWordStartPos = 0; + const int secondWordStartPos = isSpaceProximity ? (spaceProximityPos + 1) : missingSpacePos; + const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos; + const int secondWordLength = isSpaceProximity + ? (inputLength - spaceProximityPos - 1) + : (inputLength - missingSpacePos); -bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, - const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos, - const int secondWordLength, const bool isSpaceProximity) { - if (inputLength >= MAX_WORD_LENGTH) return false; + if (inputLength >= MAX_WORD_LENGTH) return; if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength) - return false; + return; + const int newWordLength = firstWordLength + secondWordLength + 1; // Allocating variable length array on stack unsigned short word[newWordLength]; @@ -620,7 +447,7 @@ bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, if (DEBUG_DICT) { LOGI("First freq: %d", firstFreq); } - if (firstFreq <= 0) return false; + if (firstFreq <= 0) return; for (int i = 0; i < firstWordLength; ++i) { word[i] = mWord[i]; @@ -630,299 +457,21 @@ bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, if (DEBUG_DICT) { LOGI("Second freq: %d", secondFreq); } - if (secondFreq <= 0) return false; + if (secondFreq <= 0) return; word[firstWordLength] = SPACE; for (int i = (firstWordLength + 1); i < newWordLength; ++i) { word[i] = mWord[i - firstWordLength - 1]; } - int pairFreq = calcFreqForSplitTwoWords(TYPED_LETTER_MULTIPLIER, firstWordLength, - secondWordLength, firstFreq, secondFreq, isSpaceProximity); + const int pairFreq = mCorrection->getFreqForSplitTwoWords(firstFreq, secondFreq); if (DEBUG_DICT) { - LOGI("Split two words: %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength, - TYPED_LETTER_MULTIPLIER); + LOGI("Split two words: %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength); } addWord(word, newWordLength, pairFreq); - return true; -} - -#ifndef NEW_DICTIONARY_FORMAT -// The following functions will be entirely replaced with new implementations. -void UnigramDictionary::getWordsOld(const int initialPos, const int inputLength, const int skipPos, - const int excessivePos, const int transposedPos,int *nextLetters, - const int nextLettersSize) { - int initialPosition = initialPos; - const int count = Dictionary::getCount(DICT_ROOT, &initialPosition); - getWordsRec(count, initialPosition, 0, - min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH), - mInputLength <= 0, 1, 0, 0, skipPos, excessivePos, transposedPos, nextLetters, - nextLettersSize); -} - -void UnigramDictionary::getWordsRec(const int childrenCount, const int pos, const int depth, - const int maxDepth, const bool traverseAllNodes, const int matchWeight, - const int inputIndex, const int diffs, const int skipPos, const int excessivePos, - const int transposedPos, int *nextLetters, const int nextLettersSize) { - int siblingPos = pos; - for (int i = 0; i < childrenCount; ++i) { - int newCount; - int newChildPosition; - bool newTraverseAllNodes; - int newMatchRate; - int newInputIndex; - int newDiffs; - int newSiblingPos; - int newOutputIndex; - const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, depth, maxDepth, - traverseAllNodes, matchWeight, inputIndex, diffs, - skipPos, excessivePos, transposedPos, - nextLetters, nextLettersSize, - &newCount, &newChildPosition, &newTraverseAllNodes, &newMatchRate, - &newInputIndex, &newDiffs, &newSiblingPos, &newOutputIndex); - siblingPos = newSiblingPos; - - if (needsToTraverseChildrenNodes) { - getWordsRec(newCount, newChildPosition, newOutputIndex, maxDepth, newTraverseAllNodes, - newMatchRate, newInputIndex, newDiffs, skipPos, excessivePos, transposedPos, - nextLetters, nextLettersSize); - } - } -} - -inline int UnigramDictionary::getMostFrequentWordLike(const int startInputIndex, - const int inputLength, unsigned short *word) { - int pos = ROOT_POS; - int count = Dictionary::getCount(DICT_ROOT, &pos); - int maxFreq = 0; - int depth = 0; - unsigned short newWord[MAX_WORD_LENGTH_INTERNAL]; - bool terminal = false; - - mStackChildCount[0] = count; - mStackSiblingPos[0] = pos; - - while (depth >= 0) { - if (mStackChildCount[depth] > 0) { - --mStackChildCount[depth]; - int firstChildPos; - int newFreq; - int siblingPos = mStackSiblingPos[depth]; - const bool needsToTraverseChildrenNodes = processCurrentNodeForExactMatch(siblingPos, - startInputIndex, depth, newWord, &firstChildPos, &count, &terminal, &newFreq, - &siblingPos); - mStackSiblingPos[depth] = siblingPos; - if (depth == (inputLength - 1)) { - // Traverse sibling node - if (terminal) { - if (newFreq > maxFreq) { - for (int i = 0; i < inputLength; ++i) word[i] = newWord[i]; - if (DEBUG_DICT && DEBUG_NODE) { -#ifdef FLAG_DBG - char s[inputLength + 1]; - for (int i = 0; i < inputLength; ++i) s[i] = word[i]; - s[inputLength] = 0; - LOGI("New missing space word found: %d > %d (%s), %d, %d", - newFreq, maxFreq, s, inputLength, depth); -#endif - } - maxFreq = newFreq; - } - } - } else if (needsToTraverseChildrenNodes) { - // Traverse children nodes - ++depth; - mStackChildCount[depth] = count; - mStackSiblingPos[depth] = firstChildPos; - } - } else { - // Traverse parent node - --depth; - } - } - - word[inputLength] = 0; - return maxFreq; + return; } -inline bool UnigramDictionary::processCurrentNodeForExactMatch(const int firstChildPos, - const int startInputIndex, const int depth, unsigned short *word, int *newChildPosition, - int *newCount, bool *newTerminal, int *newFreq, int *siblingPos) { - const int inputIndex = startInputIndex + depth; - unsigned short c; - *siblingPos = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, firstChildPos, - &c, newChildPosition, newTerminal, newFreq); - const unsigned int inputC = mProximityInfo->getPrimaryCharAt(inputIndex); - if (DEBUG_DICT) { - assert(inputC <= U_SHORT_MAX); - } - const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c); - const bool matched = (inputC == baseLowerC || inputC == c); - const bool hasChild = *newChildPosition != 0; - if (matched) { - word[depth] = c; - if (DEBUG_DICT && DEBUG_NODE) { - LOGI("Node(%c, %c)<%d>, %d, %d", inputC, c, matched, hasChild, *newFreq); - if (*newTerminal) { - LOGI("Terminal %d", *newFreq); - } - } - if (hasChild) { - *newCount = Dictionary::getCount(DICT_ROOT, newChildPosition); - return true; - } else { - return false; - } - } else { - // If this node is not user typed character, this method treats this word as unmatched. - // Thus newTerminal shouldn't be true. - *newTerminal = false; - return false; - } -} - -// TODO: use uint32_t instead of unsigned short -bool UnigramDictionary::isValidWord(unsigned short *word, int length) { - if (IS_LATEST_DICT_VERSION) { - return (getBigramPosition(DICTIONARY_HEADER_SIZE, word, 0, length) != NOT_VALID_WORD); - } else { - return (getBigramPosition(0, word, 0, length) != NOT_VALID_WORD); - } -} - - -// Require strict exact match. -int UnigramDictionary::getBigramPosition(int pos, unsigned short *word, int offset, - int length) const { - // returns address of bigram data of that word - // return -99 if not found - - int count = Dictionary::getCount(DICT_ROOT, &pos); - unsigned short currentChar = (unsigned short) word[offset]; - for (int j = 0; j < count; j++) { - unsigned short c = Dictionary::getChar(DICT_ROOT, &pos); - int terminal = Dictionary::getTerminal(DICT_ROOT, &pos); - int childPos = Dictionary::getAddress(DICT_ROOT, &pos); - if (c == currentChar) { - if (offset == length - 1) { - if (terminal) { - return (pos+1); - } - } else { - if (childPos != 0) { - int t = getBigramPosition(childPos, word, offset + 1, length); - if (t > 0) { - return t; - } - } - } - } - if (terminal) { - Dictionary::getFreq(DICT_ROOT, IS_LATEST_DICT_VERSION, &pos); - } - // There could be two instances of each alphabet - upper and lower case. So continue - // looking ... - } - return NOT_VALID_WORD; -} - -// The following functions will be modified. -inline bool UnigramDictionary::processCurrentNode(const int initialPos, const int initialDepth, - const int maxDepth, const bool initialTraverseAllNodes, int matchWeight, int inputIndex, - const int initialDiffs, const int skipPos, const int excessivePos, const int transposedPos, - int *nextLetters, const int nextLettersSize, int *newCount, int *newChildPosition, - bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs, - int *nextSiblingPosition, int *nextOutputIndex) { - if (DEBUG_DICT) { - int inputCount = 0; - if (skipPos >= 0) ++inputCount; - if (excessivePos >= 0) ++inputCount; - if (transposedPos >= 0) ++inputCount; - assert(inputCount <= 1); - } - unsigned short c; - int childPosition; - bool terminal; - int freq; - bool isSameAsUserTypedLength = false; - - const int pos = initialPos; - const int depth = initialDepth; - const int traverseAllNodes = initialTraverseAllNodes; - const int diffs = initialDiffs; - - const uint8_t flags = 0; // No flags for now - - if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex; - - *nextSiblingPosition = Dictionary::setDictionaryValues(DICT_ROOT, IS_LATEST_DICT_VERSION, pos, - &c, &childPosition, &terminal, &freq); - *nextOutputIndex = depth + 1; - - const bool needsToTraverseChildrenNodes = childPosition != 0; - - // If we are only doing traverseAllNodes, no need to look at the typed characters. - if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) { - mWord[depth] = c; - if (traverseAllNodes && terminal) { - onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos, - excessivePos, transposedPos, freq, false, nextLetters, nextLettersSize); - } - if (!needsToTraverseChildrenNodes) return false; - *newTraverseAllNodes = traverseAllNodes; - *newMatchRate = matchWeight; - *newDiffs = diffs; - *newInputIndex = inputIndex; - } else { - int inputIndexForProximity = inputIndex; - - if (transposedPos >= 0) { - if (inputIndex == transposedPos) ++inputIndexForProximity; - if (inputIndex == (transposedPos + 1)) --inputIndexForProximity; - } - - ProximityInfo::ProximityType matchedProximityCharId = mProximityInfo->getMatchedProximityId( - inputIndexForProximity, c, skipPos, excessivePos, transposedPos); - if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) return false; - mWord[depth] = c; - // If inputIndex is greater than mInputLength, that means there is no - // proximity chars. So, we don't need to check proximity. - if (ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) { - multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &matchWeight); - } - bool isSameAsUserTypedLength = mInputLength == inputIndex + 1 - || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2); - if (isSameAsUserTypedLength && terminal) { - onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos, - excessivePos, transposedPos, freq, true, nextLetters, nextLettersSize); - } - if (!needsToTraverseChildrenNodes) return false; - // Start traversing all nodes after the index exceeds the user typed length - *newTraverseAllNodes = isSameAsUserTypedLength; - *newMatchRate = matchWeight; - *newDiffs = diffs - + ((ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0); - *newInputIndex = inputIndex + 1; - } - // Optimization: Prune out words that are too long compared to how much was typed. - if (depth >= maxDepth || *newDiffs > mMaxEditDistance) { - return false; - } - - // If inputIndex is greater than mInputLength, that means there are no proximity chars. - // TODO: Check if this can be isSameAsUserTypedLength only. - if (isSameAsUserTypedLength || mInputLength <= *newInputIndex) { - *newTraverseAllNodes = true; - } - // get the count of nodes and increment childAddress. - *newCount = Dictionary::getCount(DICT_ROOT, &childPosition); - *newChildPosition = childPosition; - if (DEBUG_DICT) assert(needsToTraverseChildrenNodes); - return needsToTraverseChildrenNodes; -} - -#else // NEW_DICTIONARY_FORMAT - // Wrapper for getMostFrequentWordLikeInner, which matches it to the previous // interface. inline int UnigramDictionary::getMostFrequentWordLike(const int startInputIndex, @@ -1079,23 +628,13 @@ int UnigramDictionary::getBigramPosition(int pos, unsigned short *word, int offs // there aren't any more nodes at this level, it merely returns the address of the first byte after // the current node in nextSiblingPosition. Thus, the caller must keep count of the nodes at any // given level, as output into newCount when traversing this level's parent. -inline bool UnigramDictionary::processCurrentNode(const int initialPos, const int initialDepth, - const int maxDepth, const bool initialTraverseAllNodes, int matchWeight, int inputIndex, - const int initialDiffs, const int skipPos, const int excessivePos, const int transposedPos, - int *nextLetters, const int nextLettersSize, int *newCount, int *newChildrenPosition, - bool *newTraverseAllNodes, int *newMatchRate, int *newInputIndex, int *newDiffs, - int *nextSiblingPosition, int *newOutputIndex) { +inline bool UnigramDictionary::processCurrentNode(const int initialPos, + Correction *correction, int *newCount, + int *newChildrenPosition, int *nextSiblingPosition) { if (DEBUG_DICT) { - int inputCount = 0; - if (skipPos >= 0) ++inputCount; - if (excessivePos >= 0) ++inputCount; - if (transposedPos >= 0) ++inputCount; - assert(inputCount <= 1); + correction->checkState(); } int pos = initialPos; - int depth = initialDepth; - int traverseAllNodes = initialTraverseAllNodes; - int diffs = initialDiffs; // Flags contain the following information: // - Address type (MASK_GROUP_ADDRESS_TYPE) on two bits: @@ -1107,6 +646,9 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in // - FLAG_HAS_BIGRAMS: whether this node has bigrams or not const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(DICT_ROOT, &pos); const bool hasMultipleChars = (0 != (FLAG_HAS_MULTIPLE_CHARS & flags)); + const bool isTerminalNode = (0 != (FLAG_IS_TERMINAL & flags)); + + bool needsToInvokeOnTerminal = false; // This gets only ONE character from the stream. Next there will be: // if FLAG_HAS_MULTIPLE CHARS: the other characters of the same node @@ -1132,101 +674,21 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in const bool isLastChar = (NOT_A_CHARACTER == nextc); // If there are more chars in this nodes, then this virtual node is not a terminal. // If we are on the last char, this virtual node is a terminal if this node is. - const bool isTerminal = isLastChar && (0 != (FLAG_IS_TERMINAL & flags)); - // If there are more chars in this node, then this virtual node has children. - // If we are on the last char, this virtual node has children if this node has. - const bool hasChildren = (!isLastChar) || BinaryFormat::hasChildrenInFlags(flags); - - // This has to be done for each virtual char (this forwards the "inputIndex" which - // is the index in the user-inputted chars, as read by proximity chars. - if (excessivePos == depth && inputIndex < mInputLength - 1) ++inputIndex; - if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, depth)) { - mWord[depth] = c; - if (traverseAllNodes && isTerminal) { - // The frequency should be here, because we come here only if this is actually - // a terminal node, and we are on its last char. - const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos); - onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos, - excessivePos, transposedPos, freq, false, nextLetters, nextLettersSize); - } - if (!hasChildren) { - // If we don't have children here, that means we finished processing all - // characters of this node (we are on the last virtual node), AND we are in - // traverseAllNodes mode, which means we are searching for *completions*. We - // should skip the frequency if we have a terminal, and report the position - // of the next sibling. We don't have to return other values because we are - // returning false, as in "don't traverse children". - if (isTerminal) pos = BinaryFormat::skipFrequency(flags, pos); - *nextSiblingPosition = - BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); - return false; - } - } else { - int inputIndexForProximity = inputIndex; - - if (transposedPos >= 0) { - if (inputIndex == transposedPos) ++inputIndexForProximity; - if (inputIndex == (transposedPos + 1)) --inputIndexForProximity; - } - - int matchedProximityCharId = mProximityInfo->getMatchedProximityId( - inputIndexForProximity, c, skipPos, excessivePos, transposedPos); - if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) { - // We found that this is an unrelated character, so we should give up traversing - // this node and its children entirely. - // However we may not be on the last virtual node yet so we skip the remaining - // characters in this node, the frequency if it's there, read the next sibling - // position to output it, then return false. - // We don't have to output other values because we return false, as in - // "don't traverse children". - if (!isLastChar) { - pos = BinaryFormat::skipOtherCharacters(DICT_ROOT, pos); - } - pos = BinaryFormat::skipFrequency(flags, pos); - *nextSiblingPosition = - BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); - return false; - } - mWord[depth] = c; - // If inputIndex is greater than mInputLength, that means there is no - // proximity chars. So, we don't need to check proximity. - if (ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) { - multiplyIntCapped(TYPED_LETTER_MULTIPLIER, &matchWeight); - } - const bool isSameAsUserTypedLength = mInputLength == inputIndex + 1 - || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2); - if (isSameAsUserTypedLength && isTerminal) { - const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos); - onTerminal(mWord, depth, DICT_ROOT, flags, pos, inputIndex, matchWeight, skipPos, - excessivePos, transposedPos, freq, true, nextLetters, nextLettersSize); - } - // This character matched the typed character (enough to traverse the node at least) - // so we just evaluated it. Now we should evaluate this virtual node's children - that - // is, if it has any. If it has no children, we're done here - so we skip the end of - // the node, output the siblings position, and return false "don't traverse children". - // Note that !hasChildren implies isLastChar, so we know we don't have to skip any - // remaining char in this group for there can't be any. - if (!hasChildren) { - pos = BinaryFormat::skipFrequency(flags, pos); - *nextSiblingPosition = - BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); - return false; - } - // Start traversing all nodes after the index exceeds the user typed length - traverseAllNodes = isSameAsUserTypedLength; - diffs = diffs - + ((ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0); - // Finally, we are ready to go to the next character, the next "virtual node". - // We should advance the input index. - // We do this in this branch of the 'if traverseAllNodes' because we are still matching - // characters to input; the other branch is not matching them but searching for - // completions, this is why it does not have to do it. - ++inputIndex; - } - // Optimization: Prune out words that are too long compared to how much was typed. - if (depth >= maxDepth || diffs > mMaxEditDistance) { - // We are giving up parsing this node and its children. Skip the rest of the node, - // output the sibling position, and return that we don't want to traverse children. + const bool isTerminal = isLastChar && isTerminalNode; + + Correction::CorrectionType stateType = correction->processCharAndCalcState( + c, isTerminal); + if (stateType == Correction::TRAVERSE_ALL_ON_TERMINAL + || stateType == Correction::ON_TERMINAL) { + needsToInvokeOnTerminal = true; + } else if (stateType == Correction::UNRELATED) { + // We found that this is an unrelated character, so we should give up traversing + // this node and its children entirely. + // However we may not be on the last virtual node yet so we skip the remaining + // characters in this node, the frequency if it's there, read the next sibling + // position to output it, then return false. + // We don't have to output other values because we return false, as in + // "don't traverse children". if (!isLastChar) { pos = BinaryFormat::skipOtherCharacters(DICT_ROOT, pos); } @@ -1240,23 +702,41 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in // will take care of prefetching the next. If we finally found our last char, nextc will // contain NOT_A_CHARACTER. c = nextc; - // Also, the next char is one "virtual node" depth more than this char. - ++depth; } while (NOT_A_CHARACTER != c); - // If inputIndex is greater than mInputLength, that means there are no proximity chars. - // Here, that's all we are interested in so we don't need to check for isSameAsUserTypedLength. - if (mInputLength <= *newInputIndex) { - traverseAllNodes = true; - } + if (isTerminalNode) { + if (needsToInvokeOnTerminal) { + // The frequency should be here, because we come here only if this is actually + // a terminal node, and we are on its last char. + const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos); + onTerminal(freq, mCorrection); + } - // All the output values that are purely computation by this function are held in local - // variables. Output them to the caller. - *newTraverseAllNodes = traverseAllNodes; - *newMatchRate = matchWeight; - *newDiffs = diffs; - *newInputIndex = inputIndex; - *newOutputIndex = depth; + // If there are more chars in this node, then this virtual node has children. + // If we are on the last char, this virtual node has children if this node has. + const bool hasChildren = BinaryFormat::hasChildrenInFlags(flags); + + // This character matched the typed character (enough to traverse the node at least) + // so we just evaluated it. Now we should evaluate this virtual node's children - that + // is, if it has any. If it has no children, we're done here - so we skip the end of + // the node, output the siblings position, and return false "don't traverse children". + // Note that !hasChildren implies isLastChar, so we know we don't have to skip any + // remaining char in this group for there can't be any. + if (!hasChildren) { + pos = BinaryFormat::skipFrequency(flags, pos); + *nextSiblingPosition = + BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); + return false; + } + + // Optimization: Prune out words that are too long compared to how much was typed. + if (correction->needsToPrune()) { + pos = BinaryFormat::skipFrequency(flags, pos); + *nextSiblingPosition = + BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); + return false; + } + } // Now we finished processing this node, and we want to traverse children. If there are no // children, we can't come here. @@ -1276,6 +756,4 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in return true; } -#endif // NEW_DICTIONARY_FORMAT - } // namespace latinime diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h index 97198ef13..cfe63ff79 100644 --- a/native/src/unigram_dictionary.h +++ b/native/src/unigram_dictionary.h @@ -18,6 +18,8 @@ #define LATINIME_UNIGRAM_DICTIONARY_H #include <stdint.h> +#include "correction.h" +#include "correction_state.h" #include "defines.h" #include "proximity_info.h" @@ -30,7 +32,6 @@ namespace latinime { class UnigramDictionary { public: -#ifdef NEW_DICTIONARY_FORMAT // Mask and flags for children address type selection. static const int MASK_GROUP_ADDRESS_TYPE = 0xC0; @@ -62,23 +63,19 @@ public: static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10; static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20; static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30; -#endif // NEW_DICTIONARY_FORMAT UnigramDictionary(const uint8_t* const streamStart, int typedLetterMultipler, int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion); -#ifndef NEW_DICTIONARY_FORMAT - bool isValidWord(unsigned short *word, int length); -#else // NEW_DICTIONARY_FORMAT bool isValidWord(const uint16_t* const inWord, const int length) const; -#endif // NEW_DICTIONARY_FORMAT int getBigramPosition(int pos, unsigned short *word, int offset, int length) const; int getSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize, const int flags, unsigned short *outWords, int *frequencies); - ~UnigramDictionary(); + virtual ~UnigramDictionary(); private: + void getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize, unsigned short *outWords, int *frequencies); @@ -91,50 +88,24 @@ private: const int *ycoordinates, const int *codes, const int codesSize, unsigned short *outWords, int *frequencies); void getSuggestionCandidates(const int skipPos, const int excessivePos, - const int transposedPos, int *nextLetters, const int nextLettersSize, - const int maxDepth); + const int transposedPos); bool addWord(unsigned short *word, int length, int frequency); - bool getSplitTwoWordsSuggestion(const int inputLength, - const int firstWordStartPos, const int firstWordLength, - const int secondWordStartPos, const int secondWordLength, const bool isSpaceProximity); - bool getMissingSpaceWords(const int inputLength, const int missingSpacePos); - bool getMistypedSpaceWords(const int inputLength, const int spaceProximityPos); - int calculateFinalFreq(const int inputIndex, const int depth, const int snr, const int skipPos, - const int excessivePos, const int transposedPos, const int freq, - const bool sameLength) const; - void onTerminal(unsigned short int* word, const int depth, - const uint8_t* const root, const uint8_t flags, const int pos, - const int inputIndex, const int matchWeight, const int skipPos, - const int excessivePos, const int transposedPos, const int freq, const bool sameLength, - int *nextLetters, const int nextLettersSize); + void getSplitTwoWordsSuggestion(const int inputLength, Correction *correction); + void getMissingSpaceWords( + const int inputLength, const int missingSpacePos, Correction *correction); + void getMistypedSpaceWords( + const int inputLength, const int spaceProximityPos, Correction *correction); + void onTerminal(const int freq, Correction *correction); bool needsToSkipCurrentNode(const unsigned short c, const int inputIndex, const int skipPos, const int depth); // Process a node by considering proximity, missing and excessive character - bool processCurrentNode(const int initialPos, const int initialDepth, - const int maxDepth, const bool initialTraverseAllNodes, const int snr, int inputIndex, - const int initialDiffs, const int skipPos, const int excessivePos, - const int transposedPos, int *nextLetters, const int nextLettersSize, int *newCount, - int *newChildPosition, bool *newTraverseAllNodes, int *newSnr, int*newInputIndex, - int *newDiffs, int *nextSiblingPosition, int *nextOutputIndex); + bool processCurrentNode(const int initialPos, + Correction *correction, int *newCount, + int *newChildPosition, int *nextSiblingPosition); int getMostFrequentWordLike(const int startInputIndex, const int inputLength, unsigned short *word); -#ifndef NEW_DICTIONARY_FORMAT - void getWordsRec(const int childrenCount, const int pos, const int depth, const int maxDepth, - const bool traverseAllNodes, const int snr, const int inputIndex, const int diffs, - const int skipPos, const int excessivePos, const int transposedPos, int *nextLetters, - const int nextLettersSize); - // Keep getWordsOld for comparing performance between getWords and getWordsOld - void getWordsOld(const int initialPos, const int inputLength, const int skipPos, - const int excessivePos, const int transposedPos, int *nextLetters, - const int nextLettersSize); - // Process a node by considering missing space - bool processCurrentNodeForExactMatch(const int firstChildPos, - const int startInputIndex, const int depth, unsigned short *word, - int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos); -#else // NEW_DICTIONARY_FORMAT int getMostFrequentWordLikeInner(const uint16_t* const inWord, const int length, short unsigned int* outWord); -#endif // NEW_DICTIONARY_FORMAT const uint8_t* const DICT_ROOT; const int MAX_WORD_LENGTH; @@ -158,20 +129,15 @@ private: int *mFrequencies; unsigned short *mOutputChars; - const ProximityInfo *mProximityInfo; + ProximityInfo *mProximityInfo; + Correction *mCorrection; int mInputLength; // MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH unsigned short mWord[MAX_WORD_LENGTH_INTERNAL]; - int mMaxEditDistance; - - int mStackChildCount[MAX_WORD_LENGTH_INTERNAL]; - bool mStackTraverseAll[MAX_WORD_LENGTH_INTERNAL]; - int mStackNodeFreq[MAX_WORD_LENGTH_INTERNAL]; - int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL]; - int mStackDiffs[MAX_WORD_LENGTH_INTERNAL]; - int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL]; - int mStackOutputIndex[MAX_WORD_LENGTH_INTERNAL]; - int mNextLettersFrequency[NEXT_LETTERS_SIZE]; + + 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/res/raw/testtext.txt b/tests/res/raw/testtext.txt deleted file mode 100644 index eca20c05f..000000000 --- a/tests/res/raw/testtext.txt +++ /dev/null @@ -1,24 +0,0 @@ -This text is used as test text for measuring performance of dictionary prediction. Any text can be put into this file to test the performance (total keystroke savings). -When you think about “information,” what probably comes to mind are streams of words and numbers. Google’s pretty good at organizing these types of information, but consider all the things you can’t express with words: what does it look like in the middle of a sandstorm? What are some great examples of Art Nouveau architecture? Should I consider wedding cupcakes instead of a traditional cake? -This is why we built Google Images in 2001. We realized that for many searches, the best answer wasn’t text—it was an image or a set of images. The service has grown quite a bit since then. In 2001, we indexed around 250 million images. By 2005, we had indexed over 1 billion. And today, we have an index of over 10 billion images. -It’s not just about quantity, though. Over the past decade we’ve been baking deep computer science into Google Images to make it even faster and easier for you to find precisely the right images. We not only find images for pretty much anything you type in; we can also instantly pull out images of clip art, line drawings, faces and even colors. -There’s even more sophisticated computer vision technology powering our “Similar images” tool. For example, did you know there are nine subspecies of leopards, each with a distinct pattern of spots? Google Images can recognize the difference, returning just leopards of a particular subspecies. It can tell you the name of the subspecies in a particular image—even if that image isn’t labeled—because other similar leopard images on the web are labeled with that subspecies’s name. -And our “Similar colors” refinement doesn’t just return images based on the overall color of an image. If it did, lots of images would simply be classified as “white.” If you’re looking for [tulips] and you refine results to “white,” you really want images in which the tulips themselves are white—not the surrounding image. It takes some heavy-duty algorithmic wizardry and processing power for a search engine to understand what the items of interest are in all the images out there. -Those are just a few of the technologies we’ve built to make Google Images more useful. Meanwhile, the quantity and variety of images on the web has ballooned since 2001, and images have become one of the most popular types of content people search for. So over the next few days we’re rolling out an update to Google Images to match the scope and beauty of this fast-growing visual web, and to bring to the surface some of the powerful technology behind Images. -Here’s what’s new in this refreshed design of Google Images: -Dense tiled layout designed to make it easy to look at lots of images at once. We want to get the app out of the way so you can find what you’re really looking for. -Instant scrolling between pages, without letting you get lost in the images. You can now get up to 1,000 images, all in one scrolling page. And we’ll show small, unobtrusive page numbers so you don’t lose track of where you are. -Larger thumbnail previews on the results page, designed for modern browsers and high-res screens. -A hover pane that appears when you mouse over a given thumbnail image, giving you a larger preview, more info about the image and other image-specific features such as “Similar images.” -Once you click on an image, you’re taken to a new landing page that displays a large image in context, with the website it’s hosted on visible right behind it. Click anywhere outside the image, and you’re right in the original page where you can learn more about the source and context. -Optimized keyboard navigation for faster scrolling through many pages, taking advantage of standard web keyboard shortcuts such as Page Up / Page Down. It’s all about getting you to the info you need quickly, so you can get on with actually building that treehouse or buying those flowers. -Apple's not really ready to say it's sorry about the iPhone 4 antenna design, but it is willing to give all you darn squeaky wheels free cases for your trouble. Since Apple can't build its own Bumpers fast enough, it will give you a few options and let you decide, then send it your way for free as long as you purchased the phone before September 30th. Not good enough for you? Well, if you already bought a bumper from Apple you'll get a refund, and you can also return your phone for a full refund within 30 days as long as it's unharmed. -This solution comes at the end of 22 days of Apple engineers "working their butts off," according to Steve, with "physics" ultimately being pinned as the main culprit. Apple claims you can replicate the left-handed "death grip" bar-dropping problem on the BlackBerry Bold 9700, HTC Droid Eris, and Samsung Omnia II, and that "phones aren't perfect." Steve also claims that only 0.55% of people who bought the iPhone 4 have called into AppleCare to complain about the antenna, and the phone has a 1.7% return rate at AT&T, compared to 6% with the 3GS, though he would cop to a slight increase in dropped calls over the iPhone 3GS. For this Steve has what he confesses to be a pet theory: that 3GS users were using the case they had from the 3G, and therefore weren't met with the horrible reality of a naked, call dropping handset. Hence the free case solution, which will probably satisfy some, infuriate others, and never even blip onto the radar of many of the massive horde of consumers that's devoured this product in unprecedented numbers. -Update: Our own Richard Lai just waltzed down to the Regent Street Apple Store in London with his iPhone Bumper receipt in hand. A few minutes later he left with cold, hard cash, and kept the Bumper to boot. Seems as if the refund effort is a go, at least over in the UK. -Update 2: We've heard from several tipsters saying Apple no longer does Bumper refunds at its stores; customers will now have to make an online claim instead. Looks like we got super lucky. -If you have ever received an instant message, text message, or any text-based chat message that seemed to be written in a foreign language, this Webopedia Quick Reference will help you decipher the text chat lingo by providing the definitions to more than 1,300 chat, text message, and Twitter abbreviations. -With the popularity and rise in real-time text-based communications, such as Facebook, Twitter, instant messaging, e-mail, Internet and online gaming services, chat rooms, discussion boards and mobile phone text messaging (SMS), came the emergence of a new language tailored to the immediacy and compactness of these new communication media. -While it does seem incredible that there are so many chat abbreviations, remember that different chat abbreviations are used by different groups of people when communicating online. Some of the following chat abbreviations may be familiar to you, while others may be foreign because they are used by a group of people with different online interests and hobbies than your own. For example, people playing online games are likely to use chat abbreviations that are different than those used by someone running a financial blog updating their Twitter status. -Twitter is a free microblog, or social messaging tool that lets people stay connected through brief text message updates up to 140 characters in length. Twitter is based on you answering the question "What are you doing?" You then post thoughts, observations and goings-on during the day in answer to that question. Your update is posted on your Twitter profile page through SMS text messaging, the Twitter Web site, instant messaging, RSS, e-mail or through other social applications and sites, such as Facebook. -As with any new social medium, there is an entire vocabulary that users of the Twitter service adopt. Many of the new lingo Twitter-based terms and phrases are used to describe the collection of people who use the service, while other terms are used in reference to describe specific functions and features of the service itself. Also, there are a number of "chat terms," which are basically shorthand abbreviations that users often include in their tweets. Lastly, our guide also provides descriptions to a number of Twitter tools and applications that you can use to enhance your Twitter experience. -Here are definitions to more than 100 Twitter-related abbreviations, words, phrases, and tools that are associated with the Twitter microblogging service. If you know of a Twitter slang term or application name that is not included in our Twitter Dictionary, please let us know. diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java index 0d2802d53..1c5661bf0 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilderTests.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardLayoutParams; +import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardParams; import android.test.AndroidTestCase; @@ -41,9 +41,9 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { } public void testLayoutError() { - MiniKeyboardLayoutParams params = null; + MiniKeyboardParams params = null; try { - params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS + 1, WIDTH, HEIGHT, WIDTH * 2, + params = new MiniKeyboardParams(10, MAX_COLUMNS + 1, WIDTH, HEIGHT, WIDTH * 2, WIDTH * MAX_COLUMNS); fail("Should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -58,7 +58,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [1] public void testLayout1KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("1 key M0 columns", 1, params.mNumColumns); assertEquals("1 key M0 rows", 1, params.mNumRows); @@ -71,7 +71,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[1] public void testLayout1KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("1 key L0 columns", 1, params.mNumColumns); assertEquals("1 key L0 rows", 1, params.mNumRows); @@ -84,7 +84,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [1] public void testLayout1KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("1 key L1 columns", 1, params.mNumColumns); assertEquals("1 key L1 rows", 1, params.mNumRows); @@ -97,7 +97,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [1] public void testLayout1KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("1 key L2 columns", 1, params.mNumColumns); assertEquals("1 key L2 rows", 1, params.mNumRows); @@ -110,7 +110,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [1]| public void testLayout1KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("1 key R0 columns", 1, params.mNumColumns); assertEquals("1 key R0 rows", 1, params.mNumRows); @@ -123,7 +123,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [1] ___| public void testLayout1KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("1 key R1 columns", 1, params.mNumColumns); assertEquals("1 key R1 rows", 1, params.mNumRows); @@ -136,7 +136,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [1] ___ ___| public void testLayout1KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(1, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(1, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("1 key R2 columns", 1, params.mNumColumns); assertEquals("1 key R2 rows", 1, params.mNumRows); @@ -149,7 +149,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [1] [2] public void testLayout2KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("2 key M0 columns", 2, params.mNumColumns); assertEquals("2 key M0 rows", 1, params.mNumRows); @@ -163,7 +163,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[1] [2] public void testLayout2KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("2 key L0 columns", 2, params.mNumColumns); assertEquals("2 key L0 rows", 1, params.mNumRows); @@ -177,7 +177,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [1] [2] public void testLayout2KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("2 key L1 columns", 2, params.mNumColumns); assertEquals("2 key L1 rows", 1, params.mNumRows); @@ -191,7 +191,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [1] [2] public void testLayout2KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("2 key L2 columns", 2, params.mNumColumns); assertEquals("2 key L2 rows", 1, params.mNumRows); @@ -205,7 +205,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [2] [1]| public void testLayout2KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("2 key R0 columns", 2, params.mNumColumns); assertEquals("2 key R0 rows", 1, params.mNumRows); @@ -219,7 +219,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [2] [1] ___| public void testLayout2KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("2 key R1 columns", 2, params.mNumColumns); assertEquals("2 key R1 rows", 1, params.mNumRows); @@ -233,7 +233,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [1] [2] ___ ___| public void testLayout2KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(2, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(2, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("2 key R2 columns", 2, params.mNumColumns); assertEquals("2 key R2 rows", 1, params.mNumRows); @@ -247,7 +247,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [3] [1] [2] public void testLayout3KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("3 key columns", 3, params.mNumColumns); assertEquals("3 key rows", 1, params.mNumRows); @@ -262,7 +262,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[1] [2] [3] public void testLayout3KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("3 key L0 columns", 3, params.mNumColumns); assertEquals("3 key L0 rows", 1, params.mNumRows); @@ -277,7 +277,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [1] [2] [3] public void testLayout3KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("3 key L1 columns", 3, params.mNumColumns); assertEquals("3 key L1 rows", 1, params.mNumRows); @@ -292,7 +292,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [3] [1] [2] public void testLayout3KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("3 key L2 columns", 3, params.mNumColumns); assertEquals("3 key L2 rows", 1, params.mNumRows); @@ -307,7 +307,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [3] [2] [1]| public void testLayout3KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("3 key R0 columns", 3, params.mNumColumns); assertEquals("3 key R0 rows", 1, params.mNumRows); @@ -322,7 +322,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [3] [2] [1] ___| public void testLayout3KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("3 key R1 columns", 3, params.mNumColumns); assertEquals("3 key R1 rows", 1, params.mNumRows); @@ -337,7 +337,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [3] [1] [2] ___ ___| public void testLayout3KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(3, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(3, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("3 key R2 columns", 3, params.mNumColumns); assertEquals("3 key R2 rows", 1, params.mNumRows); @@ -352,7 +352,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [3] [1] [2] [4] public void testLayout4KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("4 key columns", 4, params.mNumColumns); assertEquals("4 key rows", 1, params.mNumRows); @@ -368,7 +368,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[1] [2] [3] [4] public void testLayout4KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("4 key L0 columns", 4, params.mNumColumns); assertEquals("4 key L0 rows", 1, params.mNumRows); @@ -384,7 +384,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [1] [2] [3] [4] public void testLayout4KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("4 key L1 columns", 4, params.mNumColumns); assertEquals("4 key L1 rows", 1, params.mNumRows); @@ -400,7 +400,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [3] [1] [2] [4] public void testLayout4KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("4 key L2 columns", 4, params.mNumColumns); assertEquals("4 key L2 rows", 1, params.mNumRows); @@ -416,7 +416,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [4] [3] [2] [1]| public void testLayout4KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("4 key R0 columns", 4, params.mNumColumns); assertEquals("4 key R0 rows", 1, params.mNumRows); @@ -432,7 +432,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [4] [3] [2] [1] ___| public void testLayout4KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("4 key R1 columns", 4, params.mNumColumns); assertEquals("4 key R1 rows", 1, params.mNumRows); @@ -448,7 +448,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [4] [3] [1] [2] ___ ___| public void testLayout4KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(4, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(4, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("4 key R2 columns", 4, params.mNumColumns); assertEquals("4 key R2 rows", 1, params.mNumRows); @@ -464,7 +464,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [5] [3] [1] [2] [4] public void testLayout5KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("5 key columns", 5, params.mNumColumns); assertEquals("5 key rows", 1, params.mNumRows); @@ -481,7 +481,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[1] [2] [3] [4] [5] public void testLayout5KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("5 key L0 columns", 5, params.mNumColumns); assertEquals("5 key L0 rows", 1, params.mNumRows); @@ -498,7 +498,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [1] [2] [3] [4] [5] public void testLayout5KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("5 key L1 columns", 5, params.mNumColumns); assertEquals("5 key L1 rows", 1, params.mNumRows); @@ -515,7 +515,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [3] [1] [2] [4] [5] public void testLayout5KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("5 key L2 columns", 5, params.mNumColumns); assertEquals("5 key L2 rows", 1, params.mNumRows); @@ -532,7 +532,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [5] [4] [3] [2] [1]| public void testLayout5KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("5 key R0 columns", 5, params.mNumColumns); assertEquals("5 key R0 rows", 1, params.mNumRows); @@ -549,7 +549,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [5] [4] [3] [2] [1] ___| public void testLayout5KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("5 key R1 columns", 5, params.mNumColumns); assertEquals("5 key R1 rows", 1, params.mNumRows); @@ -566,7 +566,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [5] [4] [3] [1] [2] ___ ___| public void testLayout5KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(5, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(5, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("5 key R2 columns", 5, params.mNumColumns); assertEquals("5 key R2 rows", 1, params.mNumRows); @@ -584,7 +584,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [6] [4] [5] // [3] [1] [2] public void testLayout6KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("6 key columns", 3, params.mNumColumns); assertEquals("6 key rows", 2, params.mNumRows); @@ -603,7 +603,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[4] [5] [6] // |[1] [2] [3] public void testLayout6KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("6 key L0 columns", 3, params.mNumColumns); assertEquals("6 key L0 rows", 2, params.mNumRows); @@ -622,7 +622,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [4] [5] [6] // |___ [1] [2] [3] public void testLayout6KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("6 key L1 columns", 3, params.mNumColumns); assertEquals("6 key L1 rows", 2, params.mNumRows); @@ -641,7 +641,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [6] [4] [5] // |___ ___ [3] [1] [2] public void testLayout6KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("6 key L2 columns", 3, params.mNumColumns); assertEquals("6 key L2 rows", 2, params.mNumRows); @@ -660,7 +660,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [6] [5] [4]| // [3] [2] [1]| public void testLayout6KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("6 key R0 columns", 3, params.mNumColumns); assertEquals("6 key R0 rows", 2, params.mNumRows); @@ -679,7 +679,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [6] [5] [4] ___| // [3] [2] [1] ___| public void testLayout6KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("6 key R1 columns", 3, params.mNumColumns); assertEquals("6 key R1 rows", 2, params.mNumRows); @@ -698,7 +698,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [6] [4] [5] ___ ___| // [3] [1] [2] ___ ___| public void testLayout6KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(6, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(6, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("6 key R2 columns", 3, params.mNumColumns); assertEquals("6 key R2 rows", 2, params.mNumRows); @@ -717,7 +717,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [5] [6] // [3] [1] [2] [4] public void testLayout7KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("7 key columns", 4, params.mNumColumns); assertEquals("7 key rows", 2, params.mNumRows); @@ -737,7 +737,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[5] [6] [7] // |[1] [2] [3] [4] public void testLayout7KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("7 key L0 columns", 4, params.mNumColumns); assertEquals("7 key L0 rows", 2, params.mNumRows); @@ -757,7 +757,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [5] [6] [7] // |___ [1] [2] [3] [4] public void testLayout7KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("7 key L1 columns", 4, params.mNumColumns); assertEquals("7 key L1 rows", 2, params.mNumRows); @@ -777,7 +777,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [7] [5] [6] // |___ ___ [3] [1] [2] [4] public void testLayout7KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("7 key L2 columns", 4, params.mNumColumns); assertEquals("7 key L2 rows", 2, params.mNumRows); @@ -797,7 +797,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [6] [5]| // [4] [3] [2] [1]| public void testLayout7KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("7 key R0 columns", 4, params.mNumColumns); assertEquals("7 key R0 rows", 2, params.mNumRows); @@ -817,7 +817,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [6] [5] ___| // [4] [3] [2] [1] ___| public void testLayout7KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("7 key R1 columns", 4, params.mNumColumns); assertEquals("7 key R1 rows", 2, params.mNumRows); @@ -837,7 +837,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [5] [6] ___ ___| // [4] [3] [1] [2] ___ ___| public void testLayout7KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("7 key R2 columns", 4, params.mNumColumns); assertEquals("7 key R2 rows", 2, params.mNumRows); @@ -856,7 +856,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [6] [5] [3] [1] [2] [4] ___| public void testLayout7KeyR3Max7() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(7, 7, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(7, 7, WIDTH, HEIGHT, XPOS_R3, KEYBOARD_WIDTH); assertEquals("7 key R2 columns", 7, params.mNumColumns); assertEquals("7 key R2 rows", 1, params.mNumRows); @@ -876,7 +876,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [5] [6] [8] // [3] [1] [2] [4] public void testLayout8KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("8 key M0 columns", 4, params.mNumColumns); assertEquals("8 key M0 rows", 2, params.mNumRows); @@ -897,7 +897,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[5] [6] [7] [8] // |[1] [2] [3] [4] public void testLayout8KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("8 key L0 columns", 4, params.mNumColumns); assertEquals("8 key L0 rows", 2, params.mNumRows); @@ -918,7 +918,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [5] [6] [7] [8] // |___ [1] [2] [3] [4] public void testLayout8KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("8 key L1 columns", 4, params.mNumColumns); assertEquals("8 key L1 rows", 2, params.mNumRows); @@ -939,7 +939,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [7] [5] [6] [8] // |___ ___ [3] [1] [2] [4] public void testLayout8KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("8 key L2 columns", 4, params.mNumColumns); assertEquals("8 key L2 rows", 2, params.mNumRows); @@ -960,7 +960,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [8] [7] [6] [5]| // [4] [3] [2] [1]| public void testLayout8KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("8 key R0 columns", 4, params.mNumColumns); assertEquals("8 key R0 rows", 2, params.mNumRows); @@ -981,7 +981,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [8] [7] [6] [5] ___| // [4] [3] [2] [1] ___| public void testLayout8KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("8 key R1 columns", 4, params.mNumColumns); assertEquals("8 key R1 rows", 2, params.mNumRows); @@ -1002,7 +1002,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [8] [7] [5] [6] ___ ___| // [4] [3] [1] [2] ___ ___| public void testLayout8KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(8, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(8, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("8 key R2 columns", 4, params.mNumColumns); assertEquals("8 key R2 rows", 2, params.mNumRows); @@ -1023,7 +1023,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [8] [6] [7] [9] // [5] [3] [1] [2] [4] public void testLayout9KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("9 key M0 columns", 5, params.mNumColumns); assertEquals("9 key M0 rows", 2, params.mNumRows); @@ -1045,7 +1045,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[6] [7] [8] [9] // |[1] [2] [3] [4] [5] public void testLayout9KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("9 key L0 columns", 5, params.mNumColumns); assertEquals("9 key L0 rows", 2, params.mNumRows); @@ -1067,7 +1067,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [6] [7] [8] [9] // |___ [1] [2] [3] [4] [5] public void testLayout9KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("9 key L1 columns", 5, params.mNumColumns); assertEquals("9 key L1 rows", 2, params.mNumRows); @@ -1089,7 +1089,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [8] [6] [7] [9] // |___ ___ [3] [1] [2] [4] [5] public void testLayout9KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("9 key L2 columns", 5, params.mNumColumns); assertEquals("9 key L2 rows", 2, params.mNumRows); @@ -1111,7 +1111,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [9] [8] [7] [6]| // [5] [4] [3] [2] [1]| public void testLayout9KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("9 key R0 columns", 5, params.mNumColumns); assertEquals("9 key R0 rows", 2, params.mNumRows); @@ -1133,7 +1133,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [9] [8] [7] [6] ___| // [5] [4] [3] [2] [1] ___| public void testLayout9KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("9 key R1 columns", 5, params.mNumColumns); assertEquals("9 key R1 rows", 2, params.mNumRows); @@ -1155,7 +1155,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [9] [8] [6] [7] ___ ___| // [5] [4] [3] [1] [2] ___ ___| public void testLayout9KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(9, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(9, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("9 key R2 columns", 5, params.mNumColumns); assertEquals("9 key R2 rows", 2, params.mNumRows); @@ -1177,7 +1177,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [A] [8] [6] [7] [9] // [5] [3] [1] [2] [4] public void testLayout10KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("10 key M0 columns", 5, params.mNumColumns); assertEquals("10 key M0 rows", 2, params.mNumRows); @@ -1200,7 +1200,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |[6] [7] [8] [9] [A] // |[1] [2] [3] [4] [5] public void testLayout10KeyL0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L0, KEYBOARD_WIDTH); assertEquals("10 key L0 columns", 5, params.mNumColumns); assertEquals("10 key L0 rows", 2, params.mNumRows); @@ -1223,7 +1223,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ [6] [7] [8] [9] [A] // |___ [1] [2] [3] [4] [5] public void testLayout10KeyL1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L1, KEYBOARD_WIDTH); assertEquals("10 key L1 columns", 5, params.mNumColumns); assertEquals("10 key L1 rows", 2, params.mNumRows); @@ -1246,7 +1246,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // |___ ___ [8] [6] [7] [9] [A] // |___ ___ [3] [1] [2] [4] [5] public void testLayout10KeyL2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_L2, KEYBOARD_WIDTH); assertEquals("10 key L2 columns", 5, params.mNumColumns); assertEquals("10 key L2 rows", 2, params.mNumRows); @@ -1269,7 +1269,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [A] [9] [8] [7] [6]| // [5] [4] [3] [2] [1]| public void testLayout10KeyR0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R0, KEYBOARD_WIDTH); assertEquals("10 key R0 columns", 5, params.mNumColumns); assertEquals("10 key R0 rows", 2, params.mNumRows); @@ -1292,7 +1292,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [A] [9] [8] [7] [6] ___| // [5] [4] [3] [2] [1] ___| public void testLayout10KeyR1() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R1, KEYBOARD_WIDTH); assertEquals("10 key R1 columns", 5, params.mNumColumns); assertEquals("10 key R1 rows", 2, params.mNumRows); @@ -1315,7 +1315,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [A] [9] [8] [6] [7] ___ ___| // [5] [4] [3] [1] [2] ___ ___| public void testLayout10KeyR2() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(10, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(10, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_R2, KEYBOARD_WIDTH); assertEquals("10 key R2 columns", 5, params.mNumColumns); assertEquals("10 key R2 rows", 2, params.mNumRows); @@ -1339,7 +1339,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [5] [6] [8] // [3] [1] [2] [4] public void testLayout11KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(11, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(11, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("11 key M0 columns", 4, params.mNumColumns); assertEquals("11 key M0 rows", 3, params.mNumRows); @@ -1364,7 +1364,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [7] [5] [6] [8] // [3] [1] [2] [4] public void testLayout12KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(12, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(12, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("12 key M0 columns", 4, params.mNumColumns); assertEquals("12 key M0 rows", 3, params.mNumRows); @@ -1391,7 +1391,7 @@ public class MiniKeyboardBuilderTests extends AndroidTestCase { // [A] [8] [6] [7] [9] // [5] [3] [1] [2] [4] public void testLayout13KeyM0() { - MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(13, MAX_COLUMNS, WIDTH, + MiniKeyboardParams params = new MiniKeyboardParams(13, MAX_COLUMNS, WIDTH, HEIGHT, XPOS_M0, KEYBOARD_WIDTH); assertEquals("13 key M0 columns", 5, params.mNumColumns); assertEquals("13 key M0 rows", 3, params.mNumRows); diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java index 07d0e5d75..48fe81715 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java +++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java @@ -16,27 +16,26 @@ package com.android.inputmethod.latin; +import android.content.Context; +import android.text.TextUtils; + import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.LatinKeyboard; -import android.content.Context; -import android.text.TextUtils; - import java.io.File; -import java.util.List; public class SuggestHelper { protected final Suggest mSuggest; - private final LatinKeyboard mKeyboard; + protected final LatinKeyboard mKeyboard; private final KeyDetector mKeyDetector; public SuggestHelper(Context context, int dictionaryId, KeyboardId keyboardId) { // Use null as the locale for Suggest so as to force it to use the internal dictionary // (and not try to find a dictionary provider for a specified locale) mSuggest = new Suggest(context, dictionaryId, null); - mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth); + mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build(); mKeyDetector = new KeyDetector(0); init(); } @@ -44,7 +43,7 @@ public class SuggestHelper { protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length, KeyboardId keyboardId) { mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null); - mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth); + mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build(); mKeyDetector = new KeyDetector(0); init(); } @@ -54,7 +53,7 @@ public class SuggestHelper { mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL); mKeyDetector.setKeyboard(mKeyboard, 0, 0); mKeyDetector.setProximityCorrectionEnabled(true); - mKeyDetector.setProximityThreshold(mKeyboard.getMostCommonKeyWidth()); + mKeyDetector.setProximityThreshold(mKeyboard.mMostCommonKeyWidth); } public void setCorrectionMode(int correctionMode) { @@ -66,8 +65,7 @@ public class SuggestHelper { } private void addKeyInfo(WordComposer word, char c) { - final List<Key> keys = mKeyboard.getKeys(); - for (final Key key : keys) { + for (final Key key : mKeyboard.mKeys) { if (key.mCode == c) { final int x = key.mX + key.mWidth / 2; final int y = key.mY + key.mHeight / 2; @@ -96,19 +94,22 @@ public class SuggestHelper { // TODO: This may be slow, but is OK for test so far. public SuggestedWords getSuggestions(CharSequence typed) { - return mSuggest.getSuggestions(null, createWordComposer(typed), null); + return mSuggest.getSuggestions(null, createWordComposer(typed), null, + mKeyboard.getProximityInfo()); } public CharSequence getFirstSuggestion(CharSequence typed) { WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null, + mKeyboard.getProximityInfo()); // Note that suggestions.getWord(0) is the word user typed. return suggestions.size() > 1 ? suggestions.getWord(1) : null; } public CharSequence getAutoCorrection(CharSequence typed) { WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null, + mKeyboard.getProximityInfo()); // Note that suggestions.getWord(0) is the word user typed. return (suggestions.size() > 1 && mSuggest.hasAutoCorrection()) ? suggestions.getWord(1) : null; @@ -116,7 +117,8 @@ public class SuggestHelper { public int getSuggestIndex(CharSequence typed, CharSequence expected) { WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null, + mKeyboard.getProximityInfo()); // Note that suggestions.getWord(0) is the word user typed. for (int i = 1; i < suggestions.size(); i++) { if (TextUtils.equals(suggestions.getWord(i), expected)) @@ -128,21 +130,23 @@ public class SuggestHelper { private void getBigramSuggestions(CharSequence previous, CharSequence typed) { if (!TextUtils.isEmpty(previous) && (typed.length() > 1)) { WordComposer firstChar = createWordComposer(Character.toString(typed.charAt(0))); - mSuggest.getSuggestions(null, firstChar, previous); + mSuggest.getSuggestions(null, firstChar, previous, mKeyboard.getProximityInfo()); } } public CharSequence getBigramFirstSuggestion(CharSequence previous, CharSequence typed) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous, + mKeyboard.getProximityInfo()); return suggestions.size() > 1 ? suggestions.getWord(1) : null; } public CharSequence getBigramAutoCorrection(CharSequence previous, CharSequence typed) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous, + mKeyboard.getProximityInfo()); return (suggestions.size() > 1 && mSuggest.hasAutoCorrection()) ? suggestions.getWord(1) : null; } @@ -151,7 +155,8 @@ public class SuggestHelper { CharSequence expected) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous, + mKeyboard.getProximityInfo()); for (int i = 1; i < suggestions.size(); i++) { if (TextUtils.equals(suggestions.getWord(i), expected)) return i; diff --git a/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java b/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java deleted file mode 100644 index 99bcc615e..000000000 --- a/tests/src/com/android/inputmethod/latin/SuggestPerformanceTests.java +++ /dev/null @@ -1,133 +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 com.android.inputmethod.latin.tests.R; - -import android.content.res.AssetFileDescriptor; -import android.text.TextUtils; -import android.util.Slog; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.util.Locale; -import java.util.StringTokenizer; - -public class SuggestPerformanceTests extends SuggestTestsBase { - private static final String TAG = SuggestPerformanceTests.class.getSimpleName(); - - private String mTestText; - private SuggestHelper mHelper; - - @Override - protected void setUp() throws Exception { - super.setUp(); - final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test); - mHelper = new SuggestHelper( - getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(), - createKeyboardId(Locale.US)); - loadString(R.raw.testtext); - } - - private void loadString(int testFileId) { - final String testFile = getTestContext().getResources().getResourceName(testFileId); - BufferedReader reader = null; - try { - reader = new BufferedReader( - new InputStreamReader(openTestRawResource(testFileId))); - final StringBuilder sb = new StringBuilder(); - String line; - Slog.i(TAG, "Reading test file " + testFile); - while ((line = reader.readLine()) != null) { - sb.append(line); - sb.append(" "); - } - mTestText = sb.toString(); - } catch (Exception e) { - Slog.e(TAG, "Can not read " + testFile); - e.printStackTrace(); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (Exception e) { - Slog.e(TAG, "Closing " + testFile + " failed"); - } - } - } - } - - /************************** Helper functions ************************/ - private int lookForBigramSuggestion(String prevWord, String currentWord) { - for (int i = 1; i < currentWord.length(); i++) { - final CharSequence prefix = currentWord.substring(0, i); - final CharSequence word = (i == 1) - ? mHelper.getBigramFirstSuggestion(prevWord, prefix) - : mHelper.getBigramAutoCorrection(prevWord, prefix); - if (TextUtils.equals(word, currentWord)) - return i; - } - return currentWord.length(); - } - - private double runText(boolean withBigrams) { - mHelper.setCorrectionMode( - withBigrams ? Suggest.CORRECTION_FULL_BIGRAM : Suggest.CORRECTION_FULL); - StringTokenizer st = new StringTokenizer(mTestText); - String prevWord = null; - int typeCount = 0; - int characterCount = 0; // without space - int wordCount = 0; - while (st.hasMoreTokens()) { - String currentWord = st.nextToken(); - boolean endCheck = false; - if (currentWord.matches("[\\w]*[\\.|?|!|*|@|&|/|:|;]")) { - currentWord = currentWord.substring(0, currentWord.length() - 1); - endCheck = true; - } - if (withBigrams && prevWord != null) { - typeCount += lookForBigramSuggestion(prevWord, currentWord); - } else { - typeCount += lookForBigramSuggestion(null, currentWord); - } - characterCount += currentWord.length(); - if (!endCheck) prevWord = currentWord; - wordCount++; - } - - double result = (double) (characterCount - typeCount) / characterCount * 100; - if (withBigrams) { - Slog.i(TAG, "with bigrams -> " + result + " % saved!"); - } else { - Slog.i(TAG, "without bigrams -> " + result + " % saved!"); - } - Slog.i(TAG, "\ttotal number of words: " + wordCount); - Slog.i(TAG, "\ttotal number of characters: " + mTestText.length()); - Slog.i(TAG, "\ttotal number of characters without space: " + characterCount); - Slog.i(TAG, "\ttotal number of characters typed: " + typeCount); - return result; - } - - - /************************** Performance Tests ************************/ - /** - * Compare the Suggest with and without bigram - * Check the log for detail - */ - public void testSuggestPerformance() { - assertTrue(runText(false) <= runText(true)); - } -} diff --git a/tests/src/com/android/inputmethod/latin/SuggestTests.java b/tests/src/com/android/inputmethod/latin/SuggestTests.java index 6e9a12797..cf4558090 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestTests.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin; import com.android.inputmethod.latin.tests.R; import android.content.res.AssetFileDescriptor; +import android.content.res.Configuration; import java.util.Locale; @@ -31,7 +32,7 @@ public class SuggestTests extends SuggestTestsBase { final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test); mHelper = new SuggestHelper( getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(), - createKeyboardId(Locale.US)); + createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT)); mHelper.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM); } diff --git a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java index 8aadee42e..058a3e7c0 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/SuggestTestsBase.java @@ -20,6 +20,7 @@ import android.content.res.AssetFileDescriptor; import android.content.res.Configuration; import android.test.AndroidTestCase; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.KeyboardId; @@ -37,12 +38,22 @@ public class SuggestTestsBase extends AndroidTestCase { mTestPackageFile = new File(getTestContext().getApplicationInfo().sourceDir); } - protected KeyboardId createKeyboardId(Locale locale) { - final int displayWidth = getContext().getResources().getDisplayMetrics().widthPixels; + protected KeyboardId createKeyboardId(Locale locale, int orientation) { + final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); + final int width; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + width = Math.max(dm.widthPixels, dm.heightPixels); + } else if (orientation == Configuration.ORIENTATION_PORTRAIT) { + width = Math.min(dm.widthPixels, dm.heightPixels); + } else { + fail("Orientation should be ORIENTATION_LANDSCAPE or ORIENTATION_PORTRAIT: " + + "orientation=" + orientation); + return null; + } return new KeyboardId(locale.toString() + " keyboard", - com.android.inputmethod.latin.R.xml.kbd_qwerty, locale, - Configuration.ORIENTATION_LANDSCAPE, displayWidth, KeyboardId.MODE_TEXT, - new EditorInfo(), false, KeyboardId.F2KEY_MODE_NONE, false, false, false, false); + com.android.inputmethod.latin.R.xml.kbd_qwerty, locale, orientation, width, + KeyboardId.MODE_TEXT, new EditorInfo(), false, KeyboardId.F2KEY_MODE_NONE, + false, false, false); } protected InputStream openTestRawResource(int resIdInTest) { diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java index 46e5a2454..a7d532e7b 100644 --- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java +++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java @@ -58,7 +58,7 @@ public class UserBigramSuggestHelper extends SuggestHelper { flushUserBigrams(); if (!TextUtils.isEmpty(previous) && !TextUtils.isEmpty(Character.toString(typed))) { WordComposer firstChar = createWordComposer(Character.toString(typed)); - mSuggest.getSuggestions(null, firstChar, previous); + mSuggest.getSuggestions(null, firstChar, previous, mKeyboard.getProximityInfo()); boolean reloading = mUserBigram.reloadDictionaryIfRequired(); if (reloading) mUserBigram.waitForDictionaryLoading(); mUserBigram.getBigrams(firstChar, previous, mSuggest); diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java index 9bd85385e..0b97e41fd 100644 --- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java +++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestTests.java @@ -15,9 +15,10 @@ */ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.tests.R; - import android.content.res.AssetFileDescriptor; +import android.content.res.Configuration; + +import com.android.inputmethod.latin.tests.R; import java.util.Locale; @@ -34,7 +35,8 @@ public class UserBigramSuggestTests extends SuggestTestsBase { final AssetFileDescriptor dict = openTestRawResourceFd(R.raw.test); mHelper = new UserBigramSuggestHelper( getContext(), mTestPackageFile, dict.getStartOffset(), dict.getLength(), - MAX_DATA, DELETE_DATA, createKeyboardId(Locale.US)); + MAX_DATA, DELETE_DATA, + createKeyboardId(Locale.US, Configuration.ORIENTATION_PORTRAIT)); } /************************** Tests ************************/ diff --git a/tools/makedict/Android.mk b/tools/makedict/Android.mk index b9fc5533d..6832b1cb6 100644 --- a/tools/makedict/Android.mk +++ b/tools/makedict/Android.mk @@ -1,5 +1,5 @@ # -# Copyright (C) 2009 The Android Open Source Project +# 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. @@ -17,8 +17,11 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_SRC_FILES += $(call all-java-files-under,tests) LOCAL_JAR_MANIFEST := etc/manifest.txt +LOCAL_MODULE_TAGS := eng LOCAL_MODULE := makedict +LOCAL_JAVA_LIBRARIES := junit include $(BUILD_HOST_JAVA_LIBRARY) include $(LOCAL_PATH)/etc/Android.mk diff --git a/tools/makedict/etc/Android.mk b/tools/makedict/etc/Android.mk index da162868a..96a90cbe0 100644 --- a/tools/makedict/etc/Android.mk +++ b/tools/makedict/etc/Android.mk @@ -1,4 +1,4 @@ -# Copyright (C) 2009 The Android Open Source Project +# 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. @@ -15,6 +15,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := eng + LOCAL_PREBUILT_EXECUTABLES := makedict include $(BUILD_HOST_PREBUILT) - diff --git a/tools/makedict/etc/makedict b/tools/makedict/etc/makedict index 8420d6e5e..7c1c02e85 100755 --- a/tools/makedict/etc/makedict +++ b/tools/makedict/etc/makedict @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright 2009, The Android Open Source Project +# 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. @@ -60,4 +60,4 @@ fi # need to use "java.ext.dirs" because "-jar" causes classpath to be ignored # might need more memory, e.g. -Xmx128M -exec java -Djava.ext.dirs="$frameworkdir" -jar "$jarpath" "$@" +exec java -ea -Djava.ext.dirs="$frameworkdir" -jar "$jarpath" "$@" diff --git a/tools/makedict/etc/manifest.txt b/tools/makedict/etc/manifest.txt index aa3a3e84c..948609da6 100644 --- a/tools/makedict/etc/manifest.txt +++ b/tools/makedict/etc/manifest.txt @@ -1 +1 @@ -Main-Class: com.android.tools.dict.MakeBinaryDictionary +Main-Class: com.android.inputmethod.latin.DictionaryMaker diff --git a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java new file mode 100644 index 000000000..92f402d3e --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java @@ -0,0 +1,1024 @@ +/* + * 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 com.android.inputmethod.latin.FusionDictionary.CharGroup; +import com.android.inputmethod.latin.FusionDictionary.Node; +import com.android.inputmethod.latin.FusionDictionary.WeightedString; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Map; +import java.util.TreeMap; + +/** + * Reads and writes XML files for a FusionDictionary. + * + * All the methods in this class are static. + */ +public class BinaryDictInputOutput { + + /* Node layout is as follows: + * | addressType xx : mask with MASK_GROUP_ADDRESS_TYPE + * 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS + * f | 01 = 1 byte : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE + * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES + * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES + * g | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS + * s | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL + * | reserved 1 bit, 1 = yes, 0 = no + * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS + * + * c | IF FLAG_HAS_MULTIPLE_CHARS + * h | char, char, char, char n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers + * a | end 1 byte, = 0 + * r | ELSE + * s | char 1 or 3 bytes + * | END + * + * f | + * r | IF FLAG_IS_TERMINAL + * e | frequency 1 byte + * q | + * + * c | IF 00 = FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = addressType + * h | // nothing + * i | ELSIF 01 = FLAG_GROUP_ADDRESS_TYPE_ONEBYTE == addressType + * l | children address, 1 byte + * d | ELSIF 10 = FLAG_GROUP_ADDRESS_TYPE_TWOBYTES == addressType + * r | children address, 2 bytes + * e | ELSE // 11 = FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = addressType + * n | children address, 3 bytes + * A | END + * d + * dress + * + * | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS + * | bigrams address list + * + * Char format is: + * 1 byte = bbbbbbbb match + * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte + * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because + * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with + * 00011111 would be outside unicode. + * else: iso-latin-1 code + * This allows for the whole unicode range to be encoded, including chars outside of + * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control + * characters which should never happen anyway (and still work, but take 3 bytes). + * + * bigram and shortcut address list is: + * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT + * | addressSign = 1 bit, : FLAG_ATTRIBUTE_OFFSET_NEGATIVE + * | 1 = must take -address, 0 = must take +address + * | xx : mask with MASK_ATTRIBUTE_ADDRESS_TYPE + * | addressFormat = 2 bits, 00 = unused : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE + * | 01 = 1 byte : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE + * | 10 = 2 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES + * | 11 = 3 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES + * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY + * <address> | IF (01 == FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE == addressFormat) + * | read 1 byte, add top 4 bits + * | ELSIF (10 == FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES == addressFormat) + * | read 2 bytes, add top 4 bits + * | ELSE // 11 == FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES == addressFormat + * | read 3 bytes, add top 4 bits + * | END + * | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address + * if (FLAG_ATTRIBUTE_HAS_NET) goto bigram_and_shortcut_address_list_is + * + */ + + private static final int MAGIC_NUMBER = 0x78B1; + private static final int VERSION = 1; + private static final int MAXIMUM_SUPPORTED_VERSION = VERSION; + // No options yet, reserved for future use. + private static final int OPTIONS = 0; + + // TODO: Make this value adaptative to content data, store it in the header, and + // use it in the reading code. + private static final int MAX_WORD_LENGTH = 48; + + private static final int MASK_GROUP_ADDRESS_TYPE = 0xC0; + private static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00; + private static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40; + private static final int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80; + private static final int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0; + + private static final int FLAG_HAS_MULTIPLE_CHARS = 0x20; + + private static final int FLAG_IS_TERMINAL = 0x10; + private static final int FLAG_HAS_BIGRAMS = 0x04; + + private static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80; + private static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40; + private static final int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30; + private static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10; + private static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20; + private static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30; + private static final int FLAG_ATTRIBUTE_FREQUENCY = 0x0F; + + private static final int GROUP_CHARACTERS_TERMINATOR = 0x1F; + + private static final int GROUP_COUNT_SIZE = 1; + private static final int GROUP_TERMINATOR_SIZE = 1; + private static final int GROUP_FLAGS_SIZE = 1; + private static final int GROUP_FREQUENCY_SIZE = 1; + private static final int GROUP_MAX_ADDRESS_SIZE = 3; + private static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1; + private static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3; + + private static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE; + private static final int INVALID_CHARACTER = -1; + + // Limiting to 127 for upward compatibility + // TODO: implement a scheme to be able to shoot 256 chargroups in a node + private static final int MAX_CHARGROUPS_IN_A_NODE = 127; + + private static final int MAX_TERMINAL_FREQUENCY = 255; + + /** + * A class grouping utility function for our specific character encoding. + */ + private static class CharEncoding { + + private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; + private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF; + + /** + * Helper method to find out whether this code fits on one byte + */ + private static boolean fitsOnOneByte(int character) { + return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE + && character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE; + } + + /** + * Compute the size of a character given its character code. + * + * Char format is: + * 1 byte = bbbbbbbb match + * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte + * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because + * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with + * 00011111 would be outside unicode. + * else: iso-latin-1 code + * This allows for the whole unicode range to be encoded, including chars outside of + * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control + * characters which should never happen anyway (and still work, but take 3 bytes). + * + * @param character the character code. + * @return the size in binary encoded-form, either 1 or 3 bytes. + */ + private static int getCharSize(int character) { + // See char encoding in FusionDictionary.java + if (fitsOnOneByte(character)) return 1; + if (INVALID_CHARACTER == character) return 1; + return 3; + } + + /** + * Compute the byte size of a character array. + */ + private static int getCharArraySize(final int[] chars) { + int size = 0; + for (int character : chars) size += getCharSize(character); + return size; + } + + /** + * Writes a char array to a byte buffer. + * + * @param characters the character array to write. + * @param buffer the byte buffer to write to. + * @param index the index in buffer to write the character array to. + * @return the index after the last character. + */ + private static int writeCharArray(int[] characters, byte[] buffer, int index) { + for (int character : characters) { + if (1 == getCharSize(character)) { + buffer[index++] = (byte)character; + } else { + buffer[index++] = (byte)(0xFF & (character >> 16)); + buffer[index++] = (byte)(0xFF & (character >> 8)); + buffer[index++] = (byte)(0xFF & character); + } + } + return index; + } + + /** + * Reads a character from the file. + * + * This follows the character format documented earlier in this source file. + * + * @param source the file, positioned over an encoded character. + * @return the character code. + */ + private static int readChar(RandomAccessFile source) throws IOException { + int character = source.readUnsignedByte(); + if (!fitsOnOneByte(character)) { + if (GROUP_CHARACTERS_TERMINATOR == character) + return INVALID_CHARACTER; + character <<= 16; + character += source.readUnsignedShort(); + } + return character; + } + } + + /** + * Compute the binary size of the character array in a group + * + * If only one character, this is the size of this character. If many, it's the sum of their + * sizes + 1 byte for the terminator. + * + * @param group the group + * @return the size of the char array, including the terminator if any + */ + private static int getGroupCharactersSize(CharGroup group) { + int size = CharEncoding.getCharArraySize(group.mChars); + if (group.hasSeveralChars()) size += GROUP_TERMINATOR_SIZE; + return size; + } + + /** + * Compute the maximum size of a CharGroup, assuming 3-byte addresses for everything. + * + * @param group the CharGroup to compute the size of. + * @return the maximum size of the group. + */ + private static int getCharGroupMaximumSize(CharGroup group) { + int size = getGroupCharactersSize(group) + GROUP_FLAGS_SIZE; + // If terminal, one byte for the frequency + if (group.isTerminal()) size += GROUP_FREQUENCY_SIZE; + size += GROUP_MAX_ADDRESS_SIZE; // For children address + if (null != group.mBigrams) { + for (WeightedString bigram : group.mBigrams) { + size += GROUP_ATTRIBUTE_FLAGS_SIZE + GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE; + } + } + return size; + } + + /** + * Compute the maximum size of a node, assuming 3-byte addresses for everything, and caches + * it in the 'actualSize' member of the node. + * + * @param node the node to compute the maximum size of. + */ + private static void setNodeMaximumSize(Node node) { + int size = GROUP_COUNT_SIZE; + for (CharGroup g : node.mData) { + final int groupSize = getCharGroupMaximumSize(g); + g.mCachedSize = groupSize; + size += groupSize; + } + node.mCachedSize = size; + } + + /** + * Helper method to hide the actual value of the no children address. + */ + private static boolean hasChildrenAddress(int address) { + return NO_CHILDREN_ADDRESS != address; + } + + /** + * Compute the size, in bytes, that an address will occupy. + * + * This can be used either for children addresses (which are always positive) or for + * attribute, which may be positive or negative but + * store their sign bit separately. + * + * @param address the address + * @return the byte size. + */ + private static int getByteSize(int address) { + assert(address < 0x1000000); + if (!hasChildrenAddress(address)) { + return 0; + } else if (Math.abs(address) < 0x100) { + return 1; + } else if (Math.abs(address) < 0x10000) { + return 2; + } else { + return 3; + } + } + // End utility methods. + + // This method is responsible for finding a nice ordering of the nodes that favors run-time + // cache performance and dictionary size. + /* package for tests */ static ArrayList<Node> flattenTree(Node root) { + final int treeSize = FusionDictionary.countCharGroups(root); + MakedictLog.i("Counted nodes : " + treeSize); + final ArrayList<Node> flatTree = new ArrayList<Node>(treeSize); + return flattenTreeInner(flatTree, root); + } + + private static ArrayList<Node> flattenTreeInner(ArrayList<Node> list, Node node) { + // Removing the node is necessary if the tails are merged, because we would then + // add the same node several times when we only want it once. A number of places in + // the code also depends on any node being only once in the list. + // Merging tails can only be done if there are no attributes. Searching for attributes + // in LatinIME code depends on a total breadth-first ordering, which merging tails + // breaks. If there are no attributes, it should be fine (and reduce the file size) + // to merge tails, and the following step would be necessary. + // If eventually the code runs on Android, searching through the whole array each time + // may be a performance concern. + list.remove(node); + list.add(node); + final ArrayList<CharGroup> branches = node.mData; + final int nodeSize = branches.size(); + for (CharGroup group : branches) { + if (null != group.mChildren) flattenTreeInner(list, group.mChildren); + } + return list; + } + + /** + * Finds the absolute address of a word in the dictionary. + * + * @param dict the dictionary in which to search. + * @param word the word we are searching for. + * @return the word address. If it is not found, an exception is thrown. + */ + private static int findAddressOfWord(final FusionDictionary dict, final String word) { + return FusionDictionary.findWordInTree(dict.mRoot, word).mCachedAddress; + } + + /** + * Computes the actual node size, based on the cached addresses of the children nodes. + * + * Each node stores its tentative address. During dictionary address computing, these + * are not final, but they can be used to compute the node size (the node size depends + * on the address of the children because the number of bytes necessary to store an + * address depends on its numeric value. + * + * @param node the node to compute the size of. + * @param dict the dictionary in which the word/attributes are to be found. + */ + private static void computeActualNodeSize(Node node, FusionDictionary dict) { + int size = GROUP_COUNT_SIZE; + for (CharGroup group : node.mData) { + int groupSize = GROUP_FLAGS_SIZE + getGroupCharactersSize(group); + if (group.isTerminal()) groupSize += GROUP_FREQUENCY_SIZE; + if (null != group.mChildren) { + final int offsetBasePoint= groupSize + node.mCachedAddress + size; + final int offset = group.mChildren.mCachedAddress - offsetBasePoint; + groupSize += getByteSize(offset); + } + if (null != group.mBigrams) { + for (WeightedString bigram : group.mBigrams) { + final int offsetBasePoint = groupSize + node.mCachedAddress + size + + GROUP_FLAGS_SIZE; + final int addressOfBigram = findAddressOfWord(dict, bigram.mWord); + final int offset = addressOfBigram - offsetBasePoint; + groupSize += getByteSize(offset) + GROUP_FLAGS_SIZE; + } + } + group.mCachedSize = groupSize; + size += groupSize; + } + node.mCachedSize = size; + } + + /** + * Computes the byte size of a list of nodes and updates each node cached position. + * + * @param flatNodes the array of nodes. + * @return the byte size of the entire stack. + */ + private static int stackNodes(ArrayList<Node> flatNodes) { + int nodeOffset = 0; + for (Node n : flatNodes) { + n.mCachedAddress = nodeOffset; + int groupOffset = 0; + for (CharGroup g : n.mData) { + g.mCachedAddress = GROUP_COUNT_SIZE + nodeOffset + groupOffset; + groupOffset += g.mCachedSize; + } + if (groupOffset + GROUP_COUNT_SIZE != n.mCachedSize) { + throw new RuntimeException("Bug : Stored and computed node size differ"); + } + nodeOffset += n.mCachedSize; + } + return nodeOffset; + } + + /** + * Compute the addresses and sizes of an ordered node array. + * + * This method takes a node array and will update its cached address and size values + * so that they can be written into a file. It determines the smallest size each of the + * nodes can be given the addresses of its children and attributes, and store that into + * each node. + * The order of the node is given by the order of the array. This method makes no effort + * to find a good order; it only mechanically computes the size this order results in. + * + * @param dict the dictionary + * @param flatNodes the ordered array of nodes + * @return the same array it was passed. The nodes have been updated for address and size. + */ + private static ArrayList<Node> computeAddresses(FusionDictionary dict, + ArrayList<Node> flatNodes) { + // First get the worst sizes and offsets + for (Node n : flatNodes) setNodeMaximumSize(n); + final int offset = stackNodes(flatNodes); + + MakedictLog.i("Compressing the array addresses. Original size : " + offset); + MakedictLog.i("(Recursively seen size : " + offset + ")"); + + int passes = 0; + boolean changesDone = false; + do { + changesDone = false; + for (Node n : flatNodes) { + final int oldNodeSize = n.mCachedSize; + computeActualNodeSize(n, dict); + final int newNodeSize = n.mCachedSize; + if (oldNodeSize < newNodeSize) throw new RuntimeException("Increased size ?!"); + if (oldNodeSize != newNodeSize) changesDone = true; + } + stackNodes(flatNodes); + ++passes; + } while (changesDone); + + final Node lastNode = flatNodes.get(flatNodes.size() - 1); + MakedictLog.i("Compression complete in " + passes + " passes."); + MakedictLog.i("After address compression : " + + (lastNode.mCachedAddress + lastNode.mCachedSize)); + + return flatNodes; + } + + /** + * Sanity-checking method. + * + * This method checks an array of node for juxtaposition, that is, it will do + * nothing if each node's cached address is actually the previous node's address + * plus the previous node's size. + * If this is not the case, it will throw an exception. + * + * @param array the array node to check + */ + private static void checkFlatNodeArray(ArrayList<Node> array) { + int offset = 0; + int index = 0; + for (Node n : array) { + if (n.mCachedAddress != offset) { + throw new RuntimeException("Wrong address for node " + index + + " : expected " + offset + ", got " + n.mCachedAddress); + } + ++index; + offset += n.mCachedSize; + } + } + + /** + * Helper method to write a variable-size address to a file. + * + * @param buffer the buffer to write to. + * @param index the index in the buffer to write the address to. + * @param address the address to write. + * @return the size in bytes the address actually took. + */ + private static int writeVariableAddress(byte[] buffer, int index, int address) { + switch (getByteSize(address)) { + case 1: + buffer[index++] = (byte)address; + return 1; + case 2: + buffer[index++] = (byte)(0xFF & (address >> 8)); + buffer[index++] = (byte)(0xFF & address); + return 2; + case 3: + buffer[index++] = (byte)(0xFF & (address >> 16)); + buffer[index++] = (byte)(0xFF & (address >> 8)); + buffer[index++] = (byte)(0xFF & address); + return 3; + case 0: + return 0; + default: + throw new RuntimeException("Address " + address + " has a strange size"); + } + } + + private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress, + final int childrenOffset) { + byte flags = 0; + if (group.mChars.length > 1) flags |= FLAG_HAS_MULTIPLE_CHARS; + if (group.mFrequency >= 0) { + flags |= FLAG_IS_TERMINAL; + } + if (null != group.mChildren) { + switch (getByteSize(childrenOffset)) { + case 1: + flags |= FLAG_GROUP_ADDRESS_TYPE_ONEBYTE; + break; + case 2: + flags |= FLAG_GROUP_ADDRESS_TYPE_TWOBYTES; + break; + case 3: + flags |= FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; + break; + default: + throw new RuntimeException("Node with a strange address"); + } + } + if (null != group.mBigrams) flags |= FLAG_HAS_BIGRAMS; + return flags; + } + + /** + * Makes the flag value for an attribute. + * + * @param more whether there are more attributes after this one. + * @param offset the offset of the attribute. + * @param frequency the frequency of the attribute, 0..15 + * @return the flags + */ + private static final int makeAttributeFlags(final boolean more, final int offset, + final int frequency) { + int bigramFlags = (more ? FLAG_ATTRIBUTE_HAS_NEXT : 0) + + (offset < 0 ? FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0); + switch (getByteSize(offset)) { + case 1: + bigramFlags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE; + break; + case 2: + bigramFlags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES; + break; + case 3: + bigramFlags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES; + break; + default: + throw new RuntimeException("Strange offset size"); + } + bigramFlags += frequency & FLAG_ATTRIBUTE_FREQUENCY; + return bigramFlags; + } + + /** + * Write a node to memory. The node is expected to have its final position cached. + * + * This can be an empty map, but the more is inside the faster the lookups will be. It can + * be carried on as long as nodes do not move. + * + * @param dict the dictionary the node is a part of (for relative offsets). + * @param buffer the memory buffer to write to. + * @param node the node to write. + * @return the address of the END of the node. + */ + private static int writePlacedNode(FusionDictionary dict, byte[] buffer, Node node) { + int index = node.mCachedAddress; + + final int size = node.mData.size(); + if (size > MAX_CHARGROUPS_IN_A_NODE) + throw new RuntimeException("A node has a group count over 127 (" + size + ")."); + + buffer[index++] = (byte)size; + int groupAddress = index; + for (int i = 0; i < size; ++i) { + CharGroup group = node.mData.get(i); + if (index != group.mCachedAddress) throw new RuntimeException("Bug: write index is not " + + "the same as the cached address of the group"); + groupAddress += GROUP_FLAGS_SIZE + getGroupCharactersSize(group); + // Sanity checks. + if (group.mFrequency > MAX_TERMINAL_FREQUENCY) { + throw new RuntimeException("A node has a frequency > " + MAX_TERMINAL_FREQUENCY + + " : " + group.mFrequency); + } + if (group.mFrequency >= 0) groupAddress += GROUP_FREQUENCY_SIZE; + final int childrenOffset = null == group.mChildren + ? NO_CHILDREN_ADDRESS : group.mChildren.mCachedAddress - groupAddress; + byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset); + buffer[index++] = flags; + index = CharEncoding.writeCharArray(group.mChars, buffer, index); + if (group.hasSeveralChars()) { + buffer[index++] = GROUP_CHARACTERS_TERMINATOR; + } + if (group.mFrequency >= 0) { + buffer[index++] = (byte) group.mFrequency; + } + final int shift = writeVariableAddress(buffer, index, childrenOffset); + index += shift; + groupAddress += shift; + + // Write bigrams + if (null != group.mBigrams) { + int remainingBigrams = group.mBigrams.size(); + for (WeightedString bigram : group.mBigrams) { + boolean more = remainingBigrams > 1; + final int addressOfBigram = findAddressOfWord(dict, bigram.mWord); + ++groupAddress; + final int offset = addressOfBigram - groupAddress; + int bigramFlags = makeAttributeFlags(more, offset, bigram.mFrequency); + buffer[index++] = (byte)bigramFlags; + final int bigramShift = writeVariableAddress(buffer, index, Math.abs(offset)); + index += bigramShift; + groupAddress += bigramShift; + --remainingBigrams; + } + } + + } + if (index != node.mCachedAddress + node.mCachedSize) throw new RuntimeException( + "Not the same size : written " + + (index - node.mCachedAddress) + " bytes out of a node that should have " + + node.mCachedSize + " bytes"); + return index; + } + + /** + * Dumps a collection of useful statistics about a node array. + * + * This prints purely informative stuff, like the total estimated file size, the + * number of nodes, of character groups, the repartition of each address size, etc + * + * @param nodes the node array. + */ + private static void showStatistics(ArrayList<Node> nodes) { + int firstTerminalAddress = Integer.MAX_VALUE; + int lastTerminalAddress = Integer.MIN_VALUE; + int size = 0; + int charGroups = 0; + int maxGroups = 0; + int maxRuns = 0; + for (Node n : nodes) { + if (maxGroups < n.mData.size()) maxGroups = n.mData.size(); + for (CharGroup cg : n.mData) { + ++charGroups; + if (cg.mChars.length > maxRuns) maxRuns = cg.mChars.length; + if (cg.mFrequency >= 0) { + if (n.mCachedAddress < firstTerminalAddress) + firstTerminalAddress = n.mCachedAddress; + if (n.mCachedAddress > lastTerminalAddress) + lastTerminalAddress = n.mCachedAddress; + } + } + if (n.mCachedAddress + n.mCachedSize > size) size = n.mCachedAddress + n.mCachedSize; + } + final int[] groupCounts = new int[maxGroups + 1]; + final int[] runCounts = new int[maxRuns + 1]; + for (Node n : nodes) { + ++groupCounts[n.mData.size()]; + for (CharGroup cg : n.mData) { + ++runCounts[cg.mChars.length]; + } + } + + MakedictLog.i("Statistics:\n" + + " total file size " + size + "\n" + + " " + nodes.size() + " nodes\n" + + " " + charGroups + " groups (" + ((float)charGroups / nodes.size()) + + " groups per node)\n" + + " first terminal at " + firstTerminalAddress + "\n" + + " last terminal at " + lastTerminalAddress + "\n" + + " Group stats : max = " + maxGroups); + for (int i = 0; i < groupCounts.length; ++i) { + MakedictLog.i(" " + i + " : " + groupCounts[i]); + } + MakedictLog.i(" Character run stats : max = " + maxRuns); + for (int i = 0; i < runCounts.length; ++i) { + MakedictLog.i(" " + i + " : " + runCounts[i]); + } + } + + /** + * Dumps a FusionDictionary to a file. + * + * This is the public entry point to write a dictionary to a file. + * + * @param destination the stream to write the binary data to. + * @param dict the dictionary to write. + */ + public static void writeDictionaryBinary(OutputStream destination, FusionDictionary dict) + throws IOException { + + // Addresses are limited to 3 bytes, so we'll just make a 16MB buffer. Since addresses + // can be relative to each node, the structure itself is not limited to 16MB at all, but + // I doubt this will ever be shot. If it is, deciding the order of the nodes becomes + // a quite complicated problem, because though the dictionary itself does not have a + // size limit, each node must still be within 16MB of all its children and parents. + // As long as this is ensured, the dictionary file may grow to any size. + // Anyway, to make a dictionary bigger than 16MB just increase the size of this buffer. + final byte[] buffer = new byte[1 << 24]; + int index = 0; + + // Magic number in big-endian order. + buffer[index++] = (byte) (0xFF & (MAGIC_NUMBER >> 8)); + buffer[index++] = (byte) (0xFF & MAGIC_NUMBER); + // Dictionary version. + buffer[index++] = (byte) (0xFF & VERSION); + // Options flags + buffer[index++] = (byte) (0xFF & (OPTIONS >> 8)); + buffer[index++] = (byte) (0xFF & OPTIONS); + + // Should we include the locale and title of the dictionary ? + + destination.write(buffer, 0, index); + index = 0; + + // Leave the choice of the optimal node order to the flattenTree function. + MakedictLog.i("Flattening the tree..."); + ArrayList<Node> flatNodes = flattenTree(dict.mRoot); + + MakedictLog.i("Computing addresses..."); + computeAddresses(dict, flatNodes); + MakedictLog.i("Checking array..."); + checkFlatNodeArray(flatNodes); + + MakedictLog.i("Writing file..."); + int dataEndOffset = 0; + for (Node n : flatNodes) { + dataEndOffset = writePlacedNode(dict, buffer, n); + } + + showStatistics(flatNodes); + + destination.write(buffer, 0, dataEndOffset); + + destination.close(); + MakedictLog.i("Done"); + } + + + // Input methods: Read a binary dictionary to memory. + // readDictionaryBinary is the public entry point for them. + + static final int[] characterBuffer = new int[MAX_WORD_LENGTH]; + private static CharGroupInfo readCharGroup(RandomAccessFile source, + final int originalGroupAddress) throws IOException { + int addressPointer = originalGroupAddress; + final int flags = source.readUnsignedByte(); + ++addressPointer; + final int characters[]; + if (0 != (flags & FLAG_HAS_MULTIPLE_CHARS)) { + int index = 0; + int character = CharEncoding.readChar(source); + addressPointer += CharEncoding.getCharSize(character); + while (-1 != character) { + characterBuffer[index++] = character; + character = CharEncoding.readChar(source); + addressPointer += CharEncoding.getCharSize(character); + } + characters = Arrays.copyOfRange(characterBuffer, 0, index); + } else { + final int character = CharEncoding.readChar(source); + addressPointer += CharEncoding.getCharSize(character); + characters = new int[] { character }; + } + final int frequency; + if (0 != (FLAG_IS_TERMINAL & flags)) { + ++addressPointer; + frequency = source.readUnsignedByte(); + } else { + frequency = CharGroup.NOT_A_TERMINAL; + } + int childrenAddress = addressPointer; + switch (flags & MASK_GROUP_ADDRESS_TYPE) { + case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE: + childrenAddress += source.readUnsignedByte(); + addressPointer += 1; + break; + case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES: + childrenAddress += source.readUnsignedShort(); + addressPointer += 2; + break; + case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES: + childrenAddress += (source.readUnsignedByte() << 16) + source.readUnsignedShort(); + addressPointer += 3; + break; + case FLAG_GROUP_ADDRESS_TYPE_NOADDRESS: + default: + childrenAddress = NO_CHILDREN_ADDRESS; + break; + } + ArrayList<PendingAttribute> bigrams = null; + if (0 != (flags & FLAG_HAS_BIGRAMS)) { + bigrams = new ArrayList<PendingAttribute>(); + boolean more = true; + while (more) { + int bigramFlags = source.readUnsignedByte(); + ++addressPointer; + more = (0 != (bigramFlags & FLAG_ATTRIBUTE_HAS_NEXT)); + final int sign = 0 == (bigramFlags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) ? 1 : -1; + int bigramAddress = addressPointer; + switch (bigramFlags & MASK_ATTRIBUTE_ADDRESS_TYPE) { + case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: + bigramAddress += sign * source.readUnsignedByte(); + addressPointer += 1; + break; + case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: + bigramAddress += sign * source.readUnsignedShort(); + addressPointer += 2; + break; + case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES: + final int offset = ((source.readUnsignedByte() << 16) + + source.readUnsignedShort()); + bigramAddress += sign * offset; + addressPointer += 3; + break; + default: + throw new RuntimeException("Has attribute with no address"); + } + bigrams.add(new PendingAttribute(bigramFlags & FLAG_ATTRIBUTE_FREQUENCY, + bigramAddress)); + } + } + return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency, + childrenAddress, bigrams); + } + + /** + * Finds, as a string, the word at the address passed as an argument. + * + * @param source the file to read from. + * @param headerSize the size of the header. + * @param address the address to seek. + * @return the word, as a string. + * @throws IOException if the file can't be read. + */ + private static String getWordAtAddress(RandomAccessFile source, long headerSize, + int address) throws IOException { + final long originalPointer = source.getFilePointer(); + source.seek(headerSize); + final int count = source.readUnsignedByte(); + int groupOffset = 1; // 1 for the group count + final StringBuilder builder = new StringBuilder(); + String result = null; + + CharGroupInfo last = null; + for (int i = count - 1; i >= 0; --i) { + CharGroupInfo info = readCharGroup(source, groupOffset); + groupOffset = info.mEndAddress; + if (info.mOriginalAddress == address) { + builder.append(new String(info.mCharacters, 0, info.mCharacters.length)); + result = builder.toString(); + break; // and return + } + if (hasChildrenAddress(info.mChildrenAddress)) { + if (info.mChildrenAddress > address) { + if (null == last) continue; + builder.append(new String(last.mCharacters, 0, last.mCharacters.length)); + source.seek(last.mChildrenAddress + headerSize); + groupOffset = last.mChildrenAddress + 1; + i = source.readUnsignedByte(); + last = null; + continue; + } + last = info; + } + if (0 == i && hasChildrenAddress(last.mChildrenAddress)) { + builder.append(new String(last.mCharacters, 0, last.mCharacters.length)); + source.seek(last.mChildrenAddress + headerSize); + groupOffset = last.mChildrenAddress + 1; + i = source.readUnsignedByte(); + last = null; + continue; + } + } + source.seek(originalPointer); + return result; + } + + /** + * Reads a single node from a binary file. + * + * This methods reads the file at the current position of its file pointer. A node is + * fully expected to start at the current position. + * This will recursively read other nodes into the structure, populating the reverse + * maps on the fly and using them to keep track of already read nodes. + * + * @param source the data file, correctly positioned at the start of a node. + * @param headerSize the size, in bytes, of the file header. + * @param reverseNodeMap a mapping from addresses to already read nodes. + * @param reverseGroupMap a mapping from addresses to already read character groups. + * @return the read node with all his children already read. + */ + private static Node readNode(RandomAccessFile source, long headerSize, + Map<Integer, Node> reverseNodeMap, Map<Integer, CharGroup> reverseGroupMap) + throws IOException { + final int nodeOrigin = (int)(source.getFilePointer() - headerSize); + final int count = source.readUnsignedByte(); + final ArrayList<CharGroup> nodeContents = new ArrayList<CharGroup>(); + int groupOffset = nodeOrigin + 1; // 1 byte for the group count + for (int i = count; i > 0; --i) { + CharGroupInfo info = readCharGroup(source, groupOffset); + ArrayList<WeightedString> bigrams = null; + if (null != info.mBigrams) { + bigrams = new ArrayList<WeightedString>(); + for (PendingAttribute bigram : info.mBigrams) { + final String word = getWordAtAddress(source, headerSize, bigram.mAddress); + bigrams.add(new WeightedString(word, bigram.mFrequency)); + } + } + if (hasChildrenAddress(info.mChildrenAddress)) { + Node children = reverseNodeMap.get(info.mChildrenAddress); + if (null == children) { + final long currentPosition = source.getFilePointer(); + source.seek(info.mChildrenAddress + headerSize); + children = readNode(source, headerSize, reverseNodeMap, reverseGroupMap); + source.seek(currentPosition); + } + nodeContents.add( + new CharGroup(info.mCharacters, bigrams, info.mFrequency, + children)); + } else { + nodeContents.add( + new CharGroup(info.mCharacters, bigrams, info.mFrequency)); + } + groupOffset = info.mEndAddress; + } + final Node node = new Node(nodeContents); + node.mCachedAddress = nodeOrigin; + reverseNodeMap.put(node.mCachedAddress, node); + return node; + } + + /** + * Reads a random access file and returns the memory representation of the dictionary. + * + * This high-level method takes a binary file and reads its contents, populating a + * FusionDictionary structure. The optional dict argument is an existing dictionary to + * which words from the file should be added. If it is null, a new dictionary is created. + * + * @param source the file to read. + * @param dict an optional dictionary to add words to, or null. + * @return the created (or merged) dictionary. + */ + public static FusionDictionary readDictionaryBinary(RandomAccessFile source, + FusionDictionary dict) throws IOException, UnsupportedFormatException { + // Check magic number + final int magic = source.readUnsignedShort(); + if (MAGIC_NUMBER != magic) { + throw new UnsupportedFormatException("The magic number in this file does not match " + + "the expected value"); + } + + // Check file version + final int version = source.readUnsignedByte(); + if (version > MAXIMUM_SUPPORTED_VERSION) { + throw new UnsupportedFormatException("This file has version " + version + + ", but this implementation does not support versions above " + + MAXIMUM_SUPPORTED_VERSION); + } + + // Read options + source.readUnsignedShort(); + + long headerSize = source.getFilePointer(); + Map<Integer, Node> reverseNodeMapping = new TreeMap<Integer, Node>(); + Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>(); + final Node root = readNode(source, headerSize, reverseNodeMapping, reverseGroupMapping); + + FusionDictionary newDict = new FusionDictionary(root, + new FusionDictionary.DictionaryOptions()); + if (null != dict) { + for (Word w : dict) { + newDict.add(w.mWord, w.mFrequency, w.mBigrams); + } + } + + return newDict; + } + + /** + * Basic test to find out whether the file is a binary dictionary or not. + * + * Concretely this only tests the magic number. + * + * @param filename The name of the file to test. + * @return true if it's a binary dictionary, false otherwise + */ + public static boolean isBinaryDictionary(String filename) { + try { + RandomAccessFile f = new RandomAccessFile(filename, "r"); + return MAGIC_NUMBER == f.readUnsignedShort(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + return false; + } + } +} diff --git a/tools/makedict/src/com/android/inputmethod/latin/CharGroupInfo.java b/tools/makedict/src/com/android/inputmethod/latin/CharGroupInfo.java new file mode 100644 index 000000000..6badfd13a --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/CharGroupInfo.java @@ -0,0 +1,45 @@ +/* + * 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 java.util.ArrayList; + +/** + * Raw char group info straight out of a file. This will contain numbers for addresses. + */ +public class CharGroupInfo { + + public final int mOriginalAddress; + public final int mEndAddress; + public final int mFlags; + public final int[] mCharacters; + public final int mFrequency; + public final int mChildrenAddress; + public final ArrayList<PendingAttribute> mBigrams; + + public CharGroupInfo(final int originalAddress, final int endAddress, final int flags, + final int[] characters, final int frequency, final int childrenAddress, + final ArrayList<PendingAttribute> bigrams) { + mOriginalAddress = originalAddress; + mEndAddress = endAddress; + mFlags = flags; + mCharacters = characters; + mFrequency = frequency; + mChildrenAddress = childrenAddress; + mBigrams = bigrams; + } +} diff --git a/tools/makedict/src/com/android/inputmethod/latin/DictionaryMaker.java b/tools/makedict/src/com/android/inputmethod/latin/DictionaryMaker.java new file mode 100644 index 000000000..1ba01075e --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/DictionaryMaker.java @@ -0,0 +1,256 @@ +/* + * 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 java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.LinkedList; + +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.SAXException; + +/** + * Main class/method for DictionaryMaker. + */ +public class DictionaryMaker { + + static class Arguments { + private final static String OPTION_VERSION_2 = "-2"; + private final static String OPTION_INPUT_SOURCE = "-s"; + private final static String OPTION_INPUT_BIGRAM_XML = "-b"; + private final static String OPTION_OUTPUT_BINARY = "-d"; + private final static String OPTION_OUTPUT_XML = "-x"; + private final static String OPTION_HELP = "-h"; + public final String mInputBinary; + public final String mInputUnigramXml; + public final String mInputBigramXml; + public final String mOutputBinary; + public final String mOutputXml; + + private void checkIntegrity() { + checkHasExactlyOneInput(); + checkHasAtLeastOneOutput(); + } + + private void checkHasExactlyOneInput() { + if (null == mInputUnigramXml && null == mInputBinary) { + throw new RuntimeException("No input file specified"); + } else if (null != mInputUnigramXml && null != mInputBinary) { + throw new RuntimeException("Both input XML and binary specified"); + } else if (null != mInputBinary && null != mInputBigramXml) { + throw new RuntimeException("Cannot specify a binary input and a separate bigram " + + "file"); + } + } + + private void checkHasAtLeastOneOutput() { + if (null == mOutputBinary && null == mOutputXml) { + throw new RuntimeException("No output specified"); + } + } + + private void displayHelp() { + MakedictLog.i("Usage: makedict " + + "[-s <unigrams.xml> [-b <bigrams.xml>] | -s <binary input>] " + + " [-d <binary output>] [-x <xml output>] [-2]\n" + + "\n" + + " Converts a source dictionary file to one or several outputs.\n" + + " Source can be an XML file, with an optional XML bigrams file, or a\n" + + " binary dictionary file.\n" + + " Both binary and XML outputs are supported. Both can be output at\n" + + " the same time but outputting several files of the same type is not\n" + + " supported."); + } + + public Arguments(String[] argsArray) { + final LinkedList<String> args = new LinkedList<String>(Arrays.asList(argsArray)); + if (args.isEmpty()) { + displayHelp(); + } + String inputBinary = null; + String inputUnigramXml = null; + String inputBigramXml = null; + String outputBinary = null; + String outputXml = null; + + while (!args.isEmpty()) { + final String arg = args.get(0); + args.remove(0); + if (arg.charAt(0) == '-') { + if (OPTION_VERSION_2.equals(arg)) { + // Do nothing, this is the default + } else if (OPTION_HELP.equals(arg)) { + displayHelp(); + } else { + // All these options need an argument + if (args.isEmpty()) { + throw new RuntimeException("Option " + arg + " requires an argument"); + } + String filename = args.get(0); + args.remove(0); + if (OPTION_INPUT_SOURCE.equals(arg)) { + if (BinaryDictInputOutput.isBinaryDictionary(filename)) { + inputBinary = filename; + } else { + inputUnigramXml = filename; + } + } else if (OPTION_INPUT_BIGRAM_XML.equals(arg)) { + inputBigramXml = filename; + } else if (OPTION_OUTPUT_BINARY.equals(arg)) { + outputBinary = filename; + } else if (OPTION_OUTPUT_XML.equals(arg)) { + outputXml = filename; + } + } + } else { + if (null == inputBinary && null == inputUnigramXml) { + if (BinaryDictInputOutput.isBinaryDictionary(arg)) { + inputBinary = arg; + } else { + inputUnigramXml = arg; + } + } else if (null == outputBinary) { + outputBinary = arg; + } else { + throw new RuntimeException("Several output binary files specified"); + } + } + } + + mInputBinary = inputBinary; + mInputUnigramXml = inputUnigramXml; + mInputBigramXml = inputBigramXml; + mOutputBinary = outputBinary; + mOutputXml = outputXml; + checkIntegrity(); + } + } + + public static void main(String[] args) + throws FileNotFoundException, ParserConfigurationException, SAXException, IOException, + UnsupportedFormatException { + final Arguments parsedArgs = new Arguments(args); + FusionDictionary dictionary = readInputFromParsedArgs(parsedArgs); + writeOutputToParsedArgs(parsedArgs, dictionary); + } + + /** + * Invoke the right input method according to args. + * + * @param args the parsed command line arguments. + * @return the read dictionary. + */ + private static FusionDictionary readInputFromParsedArgs(final Arguments args) + throws IOException, UnsupportedFormatException, ParserConfigurationException, + SAXException, FileNotFoundException { + if (null != args.mInputBinary) { + return readBinaryFile(args.mInputBinary); + } else if (null != args.mInputUnigramXml) { + return readXmlFile(args.mInputUnigramXml, args.mInputBigramXml); + } else { + throw new RuntimeException("No input file specified"); + } + } + + /** + * Read a dictionary from the name of a binary file. + * + * @param binaryFilename the name of the file in the binary dictionary format. + * @return the read dictionary. + * @throws FileNotFoundException if the file can't be found + * @throws IOException if the input file can't be read + * @throws UnsupportedFormatException if the binary file is not in the expected format + */ + private static FusionDictionary readBinaryFile(final String binaryFilename) + throws FileNotFoundException, IOException, UnsupportedFormatException { + final RandomAccessFile inputFile = new RandomAccessFile(binaryFilename, "r"); + return BinaryDictInputOutput.readDictionaryBinary(inputFile, null); + } + + /** + * Read a dictionary from a unigram XML file, and optionally a bigram XML file. + * + * @param unigramXmlFilename the name of the unigram XML file. May not be null. + * @param bigramXmlFilename the name of the bigram XML file. Pass null if there are no bigrams. + * @return the read dictionary. + * @throws FileNotFoundException if one of the files can't be found + * @throws SAXException if one or more of the XML files is not well-formed + * @throws IOException if one the input files can't be read + * @throws ParserConfigurationException if the system can't create a SAX parser + */ + private static FusionDictionary readXmlFile(final String unigramXmlFilename, + final String bigramXmlFilename) throws FileNotFoundException, SAXException, + IOException, ParserConfigurationException { + final FileInputStream unigrams = new FileInputStream(new File(unigramXmlFilename)); + final FileInputStream bigrams = null == bigramXmlFilename ? null : + new FileInputStream(new File(bigramXmlFilename)); + return XmlDictInputOutput.readDictionaryXml(unigrams, bigrams); + } + + /** + * Invoke the right output method according to args. + * + * This will write the passed dictionary to the file(s) passed in the command line arguments. + * @param args the parsed arguments. + * @param dict the file to output. + * @throws FileNotFoundException if one of the output files can't be created. + * @throws IOException if one of the output files can't be written to. + */ + private static void writeOutputToParsedArgs(final Arguments args, final FusionDictionary dict) + throws FileNotFoundException, IOException { + if (null != args.mOutputBinary) { + writeBinaryDictionary(args.mOutputBinary, dict); + } + if (null != args.mOutputXml) { + writeXmlDictionary(args.mOutputXml, dict); + } + } + + /** + * Write the dictionary in binary format to the specified filename. + * + * @param outputFilename the name of the file to write to. + * @param dict the dictionary to write. + * @throws FileNotFoundException if the output file can't be created. + * @throws IOException if the output file can't be written to. + */ + private static void writeBinaryDictionary(final String outputFilename, + final FusionDictionary dict) throws FileNotFoundException, IOException { + final File outputFile = new File(outputFilename); + BinaryDictInputOutput.writeDictionaryBinary(new FileOutputStream(outputFilename), dict); + } + + /** + * Write the dictionary in XML format to the specified filename. + * + * @param outputFilename the name of the file to write to. + * @param dict the dictionary to write. + * @throws FileNotFoundException if the output file can't be created. + * @throws IOException if the output file can't be written to. + */ + private static void writeXmlDictionary(final String outputFilename, + final FusionDictionary dict) throws FileNotFoundException, IOException { + XmlDictInputOutput.writeDictionaryXml(new FileWriter(outputFilename), dict); + } +} diff --git a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java new file mode 100644 index 000000000..031f35dfe --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java @@ -0,0 +1,602 @@ +/* + * 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * A dictionary that can fusion heads and tails of words for more compression. + */ +public class FusionDictionary implements Iterable<Word> { + + /** + * A node of the dictionary, containing several CharGroups. + * + * A node is but an ordered array of CharGroups, which essentially contain all the + * real information. + * This class also contains fields to cache size and address, to help with binary + * generation. + */ + public static class Node { + ArrayList<CharGroup> mData; + // To help with binary generation + int mCachedSize; + int mCachedAddress; + public Node() { + mData = new ArrayList<CharGroup>(); + mCachedSize = Integer.MIN_VALUE; + mCachedAddress = Integer.MIN_VALUE; + } + public Node(ArrayList<CharGroup> data) { + mData = data; + mCachedSize = Integer.MIN_VALUE; + mCachedAddress = Integer.MIN_VALUE; + } + } + + /** + * A string with a frequency. + * + * This represents an "attribute", that is either a bigram or a shortcut. + */ + public static class WeightedString { + final String mWord; + final int mFrequency; + public WeightedString(String word, int frequency) { + mWord = word; + mFrequency = frequency; + } + } + + /** + * A group of characters, with a frequency, shortcuts, bigrams, and children. + * + * This is the central class of the in-memory representation. A CharGroup is what can + * be seen as a traditional "trie node", except it can hold several characters at the + * same time. A CharGroup essentially represents one or several characters in the middle + * of the trie trie; as such, it can be a terminal, and it can have children. + * In this in-memory representation, whether the CharGroup is a terminal or not is represented + * in the frequency, where NOT_A_TERMINAL (= -1) means this is not a terminal and any other + * value is the frequency of this terminal. A terminal may have non-null shortcuts and/or + * bigrams, but a non-terminal may not. Moreover, children, if present, are null. + */ + public static class CharGroup { + public static final int NOT_A_TERMINAL = -1; + final int mChars[]; + final ArrayList<WeightedString> mBigrams; + final int mFrequency; // NOT_A_TERMINAL == mFrequency indicates this is not a terminal. + Node mChildren; + // The two following members to help with binary generation + int mCachedSize; + int mCachedAddress; + + public CharGroup(final int[] chars, + final ArrayList<WeightedString> bigrams, final int frequency) { + mChars = chars; + mFrequency = frequency; + mBigrams = bigrams; + mChildren = null; + } + + public CharGroup(final int[] chars, + final ArrayList<WeightedString> bigrams, final int frequency, final Node children) { + mChars = chars; + mFrequency = frequency; + mBigrams = bigrams; + mChildren = children; + } + + public void addChild(CharGroup n) { + if (null == mChildren) { + mChildren = new Node(); + } + mChildren.mData.add(n); + } + + public boolean isTerminal() { + return NOT_A_TERMINAL != mFrequency; + } + + public boolean hasSeveralChars() { + assert(mChars.length > 0); + return 1 < mChars.length; + } + } + + /** + * Options global to the dictionary. + * + * There are no options at the moment, so this class is empty. + */ + public static class DictionaryOptions { + } + + + public final DictionaryOptions mOptions; + public final Node mRoot; + + public FusionDictionary() { + mOptions = new DictionaryOptions(); + mRoot = new Node(); + } + + public FusionDictionary(final Node root, final DictionaryOptions options) { + mRoot = root; + mOptions = options; + } + + /** + * Helper method to convert a String to an int array. + */ + static private int[] getCodePoints(String word) { + final int wordLength = word.length(); + int[] array = new int[word.codePointCount(0, wordLength)]; + for (int i = 0; i < wordLength; ++i) { + array[i] = word.codePointAt(i); + } + return array; + } + + /** + * Helper method to add a word as a string. + * + * This method adds a word to the dictionary with the given frequency. Optional + * lists of bigrams and shortcuts can be passed here. For each word inside, + * they will be added to the dictionary as necessary. + * + * @param word the word to add. + * @param frequency the frequency of the word, in the range [0..255]. + * @param bigrams a list of bigrams, or null. + */ + public void add(String word, int frequency, ArrayList<WeightedString> bigrams) { + if (null != bigrams) { + for (WeightedString bigram : bigrams) { + final CharGroup t = findWordInTree(mRoot, bigram.mWord); + if (null == t) { + add(getCodePoints(bigram.mWord), 0, null); + } + } + } + add(getCodePoints(word), frequency, bigrams); + } + + /** + * Sanity check for a node. + * + * This method checks that all CharGroups in a node are ordered as expected. + * If they are, nothing happens. If they aren't, an exception is thrown. + */ + private void checkStack(Node node) { + ArrayList<CharGroup> stack = node.mData; + int lastValue = -1; + for (int i = 0; i < stack.size(); ++i) { + int currentValue = stack.get(i).mChars[0]; + if (currentValue <= lastValue) + throw new RuntimeException("Invalid stack"); + else + lastValue = currentValue; + } + } + + /** + * Add a word to this dictionary. + * + * The bigrams, if any, have to be in the dictionary already. If they aren't, + * an exception is thrown. + * + * @param word the word, as an int array. + * @param frequency the frequency of the word, in the range [0..255]. + * @param bigrams an optional list of bigrams for this word (null if none). + */ + private void add(int[] word, int frequency, ArrayList<WeightedString> bigrams) { + assert(frequency >= 0 && frequency <= 255); + Node currentNode = mRoot; + int charIndex = 0; + + CharGroup currentGroup = null; + int differentCharIndex = 0; // Set by the loop to the index of the char that differs + int nodeIndex = findIndexOfChar(mRoot, word[charIndex]); + while (CHARACTER_NOT_FOUND != nodeIndex) { + currentGroup = currentNode.mData.get(nodeIndex); + differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex) ; + if (ARRAYS_ARE_EQUAL != differentCharIndex + && differentCharIndex < currentGroup.mChars.length) break; + if (null == currentGroup.mChildren) break; + charIndex += currentGroup.mChars.length; + if (charIndex >= word.length) break; + currentNode = currentGroup.mChildren; + nodeIndex = findIndexOfChar(currentNode, word[charIndex]); + } + + if (-1 == nodeIndex) { + // No node at this point to accept the word. Create one. + final int insertionIndex = findInsertionIndex(currentNode, word[charIndex]); + final CharGroup newGroup = new CharGroup( + Arrays.copyOfRange(word, charIndex, word.length), bigrams, frequency); + currentNode.mData.add(insertionIndex, newGroup); + checkStack(currentNode); + } else { + // There is a word with a common prefix. + if (differentCharIndex == currentGroup.mChars.length) { + if (charIndex + differentCharIndex >= word.length) { + // The new word is a prefix of an existing word, but the node on which it + // should end already exists as is. + if (currentGroup.mFrequency > 0) { + throw new RuntimeException("Such a word already exists in the dictionary : " + + new String(word, 0, word.length)); + } else { + final CharGroup newNode = new CharGroup(currentGroup.mChars, + bigrams, frequency, currentGroup.mChildren); + currentNode.mData.set(nodeIndex, newNode); + checkStack(currentNode); + } + } else { + // The new word matches the full old word and extends past it. + // We only have to create a new node and add it to the end of this. + final CharGroup newNode = new CharGroup( + Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), + bigrams, frequency); + currentGroup.mChildren = new Node(); + currentGroup.mChildren.mData.add(newNode); + } + } else { + if (0 == differentCharIndex) { + // Exact same word. Check the frequency is 0 or -1, and update. + if (0 != frequency) { + if (0 < currentGroup.mFrequency) { + throw new RuntimeException("This word already exists with frequency " + + currentGroup.mFrequency + " : " + + new String(word, 0, word.length)); + } + final CharGroup newGroup = new CharGroup(word, + currentGroup.mBigrams, frequency); + currentNode.mData.set(nodeIndex, newGroup); + } + } else { + // Partial prefix match only. We have to replace the current node with a node + // containing the current prefix and create two new ones for the tails. + Node newChildren = new Node(); + final CharGroup newOldWord = new CharGroup( + Arrays.copyOfRange(currentGroup.mChars, differentCharIndex, + currentGroup.mChars.length), + currentGroup.mBigrams, currentGroup.mFrequency, currentGroup.mChildren); + newChildren.mData.add(newOldWord); + + final CharGroup newParent; + if (charIndex + differentCharIndex >= word.length) { + newParent = new CharGroup( + Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex), + bigrams, frequency, newChildren); + } else { + newParent = new CharGroup( + Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex), + null, -1, newChildren); + final CharGroup newWord = new CharGroup( + Arrays.copyOfRange(word, charIndex + differentCharIndex, + word.length), bigrams, frequency); + final int addIndex = word[charIndex + differentCharIndex] + > currentGroup.mChars[differentCharIndex] ? 1 : 0; + newChildren.mData.add(addIndex, newWord); + } + currentNode.mData.set(nodeIndex, newParent); + } + checkStack(currentNode); + } + } + } + + /** + * Custom comparison of two int arrays taken to contain character codes. + * + * This method compares the two arrays passed as an argument in a lexicographic way, + * with an offset in the dst string. + * This method does NOT test for the first character. It is taken to be equal. + * I repeat: this method starts the comparison at 1 <> dstOffset + 1. + * The index where the strings differ is returned. ARRAYS_ARE_EQUAL = 0 is returned if the + * strings are equal. This works BECAUSE we don't look at the first character. + * + * @param src the left-hand side string of the comparison. + * @param dst the right-hand side string of the comparison. + * @param dstOffset the offset in the right-hand side string. + * @return the index at which the strings differ, or ARRAYS_ARE_EQUAL = 0 if they don't. + */ + private static int ARRAYS_ARE_EQUAL = 0; + private static int compareArrays(final int[] src, final int[] dst, int dstOffset) { + // We do NOT test the first char, because we come from a method that already + // tested it. + for (int i = 1; i < src.length; ++i) { + if (dstOffset + i >= dst.length) return i; + if (src[i] != dst[dstOffset + i]) return i; + } + if (dst.length > src.length) return src.length; + return ARRAYS_ARE_EQUAL; + } + + /** + * Helper class that compares and sorts two chargroups according to their + * first element only. I repeat: ONLY the first element is considered, the rest + * is ignored. + * This comparator imposes orderings that are inconsistent with equals. + */ + static private class CharGroupComparator implements java.util.Comparator { + public int compare(Object o1, Object o2) { + final CharGroup c1 = (CharGroup)o1; + final CharGroup c2 = (CharGroup)o2; + if (c1.mChars[0] == c2.mChars[0]) return 0; + return c1.mChars[0] < c2.mChars[0] ? -1 : 1; + } + public boolean equals(Object o) { + return o instanceof CharGroupComparator; + } + } + final static private CharGroupComparator CHARGROUP_COMPARATOR = new CharGroupComparator(); + + /** + * Finds the insertion index of a character within a node. + */ + private static int findInsertionIndex(final Node node, int character) { + final List data = node.mData; + final CharGroup reference = new CharGroup(new int[] { character }, null, 0); + int result = Collections.binarySearch(data, reference, CHARGROUP_COMPARATOR); + return result >= 0 ? result : -result - 1; + } + + /** + * Find the index of a char in a node, if it exists. + * + * @param node the node to search in. + * @param character the character to search for. + * @return the position of the character if it's there, or CHARACTER_NOT_FOUND = -1 else. + */ + private static int CHARACTER_NOT_FOUND = -1; + private static int findIndexOfChar(final Node node, int character) { + final int insertionIndex = findInsertionIndex(node, character); + if (node.mData.size() <= insertionIndex) return CHARACTER_NOT_FOUND; + return character == node.mData.get(insertionIndex).mChars[0] ? insertionIndex + : CHARACTER_NOT_FOUND; + } + + /** + * Helper method to find a word in a given branch. + */ + public static CharGroup findWordInTree(Node node, final String s) { + int index = 0; + final StringBuilder checker = new StringBuilder(); + + CharGroup currentGroup; + do { + int indexOfGroup = findIndexOfChar(node, s.codePointAt(index)); + if (CHARACTER_NOT_FOUND == indexOfGroup) return null; + currentGroup = node.mData.get(indexOfGroup); + checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length)); + index += currentGroup.mChars.length; + if (index < s.length()) { + node = currentGroup.mChildren; + } + } while (null != node && index < s.length()); + + if (!s.equals(checker.toString())) return null; + return currentGroup; + } + + /** + * Recursively count the number of character groups in a given branch of the trie. + * + * @param node the parent node. + * @return the number of char groups in all the branch under this node. + */ + public static int countCharGroups(final Node node) { + final int nodeSize = node.mData.size(); + int size = nodeSize; + for (int i = nodeSize - 1; i >= 0; --i) { + CharGroup group = node.mData.get(i); + if (null != group.mChildren) + size += countCharGroups(group.mChildren); + } + return size; + } + + /** + * Recursively count the number of nodes in a given branch of the trie. + * + * @param node the node to count. + * @result the number of nodes in this branch. + */ + public static int countNodes(final Node node) { + int size = 1; + for (int i = node.mData.size() - 1; i >= 0; --i) { + CharGroup group = node.mData.get(i); + if (null != group.mChildren) + size += countNodes(group.mChildren); + } + return size; + } + + // Historically, the tails of the words were going to be merged to save space. + // However, that would prevent the code to search for a specific address in log(n) + // time so this was abandoned. + // The code is still of interest as it does add some compression to any dictionary + // that has no need for attributes. Implementations that does not read attributes should be + // able to read a dictionary with merged tails. + // Also, the following code does support frequencies, as in, it will only merges + // tails that share the same frequency. Though it would result in the above loss of + // performance while searching by address, it is still technically possible to merge + // tails that contain attributes, but this code does not take that into account - it does + // not compare attributes and will merge terminals with different attributes regardless. + public void mergeTails() { + MakedictLog.i("Do not merge tails"); + return; + +// MakedictLog.i("Merging nodes. Number of nodes : " + countNodes(root)); +// MakedictLog.i("Number of groups : " + countCharGroups(root)); +// +// final HashMap<String, ArrayList<Node>> repository = +// new HashMap<String, ArrayList<Node>>(); +// mergeTailsInner(repository, root); +// +// MakedictLog.i("Number of different pseudohashes : " + repository.size()); +// int size = 0; +// for (ArrayList<Node> a : repository.values()) { +// size += a.size(); +// } +// MakedictLog.i("Number of nodes after merge : " + (1 + size)); +// MakedictLog.i("Recursively seen nodes : " + countNodes(root)); + } + + // The following methods are used by the deactivated mergeTails() +// private static boolean isEqual(Node a, Node b) { +// if (null == a && null == b) return true; +// if (null == a || null == b) return false; +// if (a.data.size() != b.data.size()) return false; +// final int size = a.data.size(); +// for (int i = size - 1; i >= 0; --i) { +// CharGroup aGroup = a.data.get(i); +// CharGroup bGroup = b.data.get(i); +// if (aGroup.frequency != bGroup.frequency) return false; +// if (aGroup.alternates == null && bGroup.alternates != null) return false; +// if (aGroup.alternates != null && !aGroup.equals(bGroup.alternates)) return false; +// if (!Arrays.equals(aGroup.chars, bGroup.chars)) return false; +// if (!isEqual(aGroup.children, bGroup.children)) return false; +// } +// return true; +// } + +// static private HashMap<String, ArrayList<Node>> mergeTailsInner( +// final HashMap<String, ArrayList<Node>> map, final Node node) { +// final ArrayList<CharGroup> branches = node.data; +// final int nodeSize = branches.size(); +// for (int i = 0; i < nodeSize; ++i) { +// CharGroup group = branches.get(i); +// if (null != group.children) { +// String pseudoHash = getPseudoHash(group.children); +// ArrayList<Node> similarList = map.get(pseudoHash); +// if (null == similarList) { +// similarList = new ArrayList<Node>(); +// map.put(pseudoHash, similarList); +// } +// boolean merged = false; +// for (Node similar : similarList) { +// if (isEqual(group.children, similar)) { +// group.children = similar; +// merged = true; +// break; +// } +// } +// if (!merged) { +// similarList.add(group.children); +// } +// mergeTailsInner(map, group.children); +// } +// } +// return map; +// } + +// private static String getPseudoHash(final Node node) { +// StringBuilder s = new StringBuilder(); +// for (CharGroup g : node.data) { +// s.append(g.frequency); +// for (int ch : g.chars){ +// s.append(Character.toChars(ch)); +// } +// } +// return s.toString(); +// } + + /** + * Iterator to walk through a dictionary. + * + * This is purely for convenience. + */ + public static class DictionaryIterator implements Iterator<Word> { + + private static class Position { + public Iterator<CharGroup> pos; + public int length; + public Position(ArrayList<CharGroup> groups) { + pos = groups.iterator(); + length = 0; + } + } + final StringBuilder mCurrentString; + final LinkedList<Position> mPositions; + + public DictionaryIterator(ArrayList<CharGroup> root) { + mCurrentString = new StringBuilder(); + mPositions = new LinkedList<Position>(); + final Position rootPos = new Position(root); + mPositions.add(rootPos); + } + + @Override + public boolean hasNext() { + for (Position p : mPositions) { + if (p.pos.hasNext()) { + return true; + } + } + return false; + } + + @Override + public Word next() { + Position currentPos = mPositions.getLast(); + mCurrentString.setLength(mCurrentString.length() - currentPos.length); + + do { + if (currentPos.pos.hasNext()) { + final CharGroup currentGroup = currentPos.pos.next(); + currentPos.length = currentGroup.mChars.length; + for (int i : currentGroup.mChars) + mCurrentString.append(Character.toChars(i)); + if (null != currentGroup.mChildren) { + currentPos = new Position(currentGroup.mChildren.mData); + mPositions.addLast(currentPos); + } + if (currentGroup.mFrequency >= 0) + return new Word(mCurrentString.toString(), currentGroup.mFrequency, + currentGroup.mBigrams); + } else { + mPositions.removeLast(); + currentPos = mPositions.getLast(); + mCurrentString.setLength(mCurrentString.length() - mPositions.getLast().length); + } + } while(true); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Unsupported yet"); + } + + } + + /** + * Method to return an iterator. + * + * This method enables Java's enhanced for loop. With this you can have a FusionDictionary x + * and say : for (Word w : x) {} + */ + @Override + public Iterator<Word> iterator() { + return new DictionaryIterator(mRoot.mData); + } +} diff --git a/tools/makedict/src/com/android/inputmethod/latin/MakedictLog.java b/tools/makedict/src/com/android/inputmethod/latin/MakedictLog.java new file mode 100644 index 000000000..badb2ff3d --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/MakedictLog.java @@ -0,0 +1,40 @@ +/* + * 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; + +/** + * Wrapper to redirect log events to the right output medium. + */ +public class MakedictLog { + + private static void print(String message) { + System.out.println(message); + } + + public static void d(String message) { + print(message); + } + public static void e(String message) { + print(message); + } + public static void i(String message) { + print(message); + } + public static void w(String message) { + print(message); + } +} diff --git a/tools/makedict/src/com/android/inputmethod/latin/PendingAttribute.java b/tools/makedict/src/com/android/inputmethod/latin/PendingAttribute.java new file mode 100644 index 000000000..e50202184 --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/PendingAttribute.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * A not-yet-resolved attribute. + * + * An attribute is either a bigram or an shortcut. + * All instances of this class are always immutable. + */ +public class PendingAttribute { + public final int mFrequency; + public final int mAddress; + public PendingAttribute(final int frequency, final int address) { + mFrequency = frequency; + mAddress = address; + } +} diff --git a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java b/tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java index eb740e111..511ad5675 100644 --- a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java +++ b/tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java @@ -16,14 +16,11 @@ package com.android.inputmethod.latin; -import android.content.Context; - -import java.util.List; -import java.util.Locale; - -class PrivateBinaryDictionaryGetter { - private PrivateBinaryDictionaryGetter() {} - public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context) { - return null; +/** + * Simple exception thrown when a file format is not recognized. + */ +public class UnsupportedFormatException extends Exception { + public UnsupportedFormatException(String description) { + super(description); } } diff --git a/tools/makedict/src/com/android/inputmethod/latin/Word.java b/tools/makedict/src/com/android/inputmethod/latin/Word.java new file mode 100644 index 000000000..916165a41 --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/Word.java @@ -0,0 +1,65 @@ +/* + * 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 com.android.inputmethod.latin.FusionDictionary.WeightedString; + +import java.util.ArrayList; + +/** + * Utility class for a word with a frequency. + * + * This is chiefly used to iterate a dictionary. + */ +public class Word implements Comparable<Word> { + final String mWord; + final int mFrequency; + final ArrayList<WeightedString> mBigrams; + + public Word(String word, int frequency, ArrayList<WeightedString> bigrams) { + mWord = word; + mFrequency = frequency; + mBigrams = bigrams; + } + + /** + * Three-way comparison. + * + * A Word x is greater than a word y if x has a higher frequency. If they have the same + * frequency, they are sorted in lexicographic order. + */ + @Override + public int compareTo(Word w) { + if (mFrequency < w.mFrequency) return 1; + if (mFrequency > w.mFrequency) return -1; + return mWord.compareTo(w.mWord); + } + + /** + * Equality test. + * + * Words are equal if they have the same frequency, the same spellings, and the same + * attributes. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Word)) return false; + Word w = (Word)o; + return mFrequency == w.mFrequency && mWord.equals(w.mWord) + && mBigrams.equals(w.mBigrams); + } +} diff --git a/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java new file mode 100644 index 000000000..096bfd182 --- /dev/null +++ b/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java @@ -0,0 +1,215 @@ +/* + * 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 com.android.inputmethod.latin.FusionDictionary.WeightedString; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.TreeSet; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Reads and writes XML files for a FusionDictionary. + * + * All functions in this class are static. + */ +public class XmlDictInputOutput { + + private static final String WORD_TAG = "w"; + private static final String BIGRAM_TAG = "bigram"; + private static final String FREQUENCY_ATTR = "f"; + private static final String WORD_ATTR = "word"; + + /** + * SAX handler for a unigram XML file. + */ + static private class UnigramHandler extends DefaultHandler { + // Parser states + private static final int NONE = 0; + private static final int START = 1; + private static final int WORD = 2; + private static final int BIGRAM = 4; + private static final int END = 5; + private static final int UNKNOWN = 6; + + final FusionDictionary mDictionary; + int mState; // the state of the parser + int mFreq; // the currently read freq + final HashMap<String, ArrayList<WeightedString>> mBigramsMap; + + /** + * Create the handler. + * + * @param dict the dictionary to construct. + * @param bigrams the bigrams as a map. This may be empty, but may not be null. + */ + public UnigramHandler(FusionDictionary dict, + HashMap<String, ArrayList<WeightedString>> bigrams) { + mDictionary = dict; + mBigramsMap = bigrams; + mState = START; + mFreq = 0; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attrs) { + if (WORD_TAG.equals(localName)) { + mState = WORD; + for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) { + final String attrName = attrs.getLocalName(attrIndex); + if (FREQUENCY_ATTR.equals(attrName)) { + mFreq = Integer.parseInt(attrs.getValue(attrIndex)); + } + } + } else { + mState = UNKNOWN; + } + } + + @Override + public void characters(char[] ch, int start, int length) { + if (WORD == mState) { + final String word = String.copyValueOf(ch, start, length); + mDictionary.add(word, mFreq, mBigramsMap.get(word)); + } + } + + @Override + public void endElement(String uri, String localName, String qName) { + if (WORD == mState) mState = START; + } + } + + /** + * SAX handler for a bigram XML file. + */ + static private class BigramHandler extends DefaultHandler { + private final static String BIGRAM_W1_TAG = "bi"; + private final static String BIGRAM_W2_TAG = "w"; + private final static String BIGRAM_W1_ATTRIBUTE = "w1"; + private final static String BIGRAM_W2_ATTRIBUTE = "w2"; + private final static String BIGRAM_FREQ_ATTRIBUTE = "p"; + + String mW1; + final HashMap<String, ArrayList<WeightedString>> mBigramsMap; + + public BigramHandler() { + mW1 = null; + mBigramsMap = new HashMap<String, ArrayList<WeightedString>>(); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attrs) { + if (BIGRAM_W1_TAG.equals(localName)) { + mW1 = attrs.getValue(uri, BIGRAM_W1_ATTRIBUTE); + } else if (BIGRAM_W2_TAG.equals(localName)) { + String w2 = attrs.getValue(uri, BIGRAM_W2_ATTRIBUTE); + int freq = Integer.parseInt(attrs.getValue(uri, BIGRAM_FREQ_ATTRIBUTE)); + WeightedString bigram = new WeightedString(w2, freq / 8); + ArrayList<WeightedString> bigramList = mBigramsMap.get(mW1); + if (null == bigramList) bigramList = new ArrayList<WeightedString>(); + bigramList.add(bigram); + mBigramsMap.put(mW1, bigramList); + } + } + + public HashMap<String, ArrayList<WeightedString>> getBigramMap() { + return mBigramsMap; + } + } + + /** + * Reads a dictionary from an XML file. + * + * This is the public method that will parse an XML file and return the corresponding memory + * representation. + * + * @param unigrams the file to read the data from. + * @return the in-memory representation of the dictionary. + */ + public static FusionDictionary readDictionaryXml(InputStream unigrams, InputStream bigrams) + throws SAXException, IOException, ParserConfigurationException { + final SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + final SAXParser parser = factory.newSAXParser(); + final BigramHandler bigramHandler = new BigramHandler(); + if (null != bigrams) parser.parse(bigrams, bigramHandler); + + final FusionDictionary dict = new FusionDictionary(); + final UnigramHandler unigramHandler = + new UnigramHandler(dict, bigramHandler.getBigramMap()); + parser.parse(unigrams, unigramHandler); + return dict; + } + + /** + * Reads a dictionary in the first, legacy XML format + * + * This method reads data from the parser and creates a new FusionDictionary with it. + * The format parsed by this method is the format used before Ice Cream Sandwich, + * which has no support for bigrams or shortcuts. + * It is important to note that this method expects the parser to have already eaten + * the first, all-encompassing tag. + * + * @param xpp the parser to read the data from. + * @return the parsed dictionary. + */ + + /** + * Writes a dictionary to an XML file. + * + * The output format is the "second" format, which supports bigrams and shortcuts. + * + * @param destination a destination stream to write to. + * @param dict the dictionary to write. + */ + public static void writeDictionaryXml(Writer destination, FusionDictionary dict) + throws IOException { + final TreeSet<Word> set = new TreeSet<Word>(); + for (Word word : dict) { + set.add(word); + } + // TODO: use an XMLSerializer if this gets big + destination.write("<wordlist format=\"2\">\n"); + for (Word word : set) { + destination.write(" <" + WORD_TAG + " " + WORD_ATTR + "=\"" + word.mWord + "\" " + + FREQUENCY_ATTR + "=\"" + word.mFrequency + "\">"); + if (null != word.mBigrams) { + destination.write("\n"); + for (WeightedString bigram : word.mBigrams) { + destination.write(" <" + BIGRAM_TAG + " " + FREQUENCY_ATTR + "=\"" + + bigram.mFrequency + "\">" + bigram.mWord + "</" + BIGRAM_TAG + ">\n"); + } + destination.write(" "); + } + destination.write("</" + WORD_TAG + ">\n"); + } + destination.write("</wordlist>\n"); + destination.close(); + } +} diff --git a/tools/makedict/src/com/android/tools/dict/BigramDictionary.java b/tools/makedict/src/com/android/tools/dict/BigramDictionary.java deleted file mode 100644 index 35115bf2c..000000000 --- a/tools/makedict/src/com/android/tools/dict/BigramDictionary.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.dict; - -import org.xml.sax.Attributes; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Helper for MakeBinaryDictionary - * Deals with all the bigram data - */ -public class BigramDictionary { - - /* - * Must match the values in the client side which is located in dictionary.cpp & dictionary.h - * Changing these values will generate totally different structure which must be also reflected - * on the client side. - */ - public static final int FLAG_BIGRAM_READ = 0x80; - public static final int FLAG_BIGRAM_CHILDEXIST = 0x40; - public static final int FLAG_BIGRAM_CONTINUED = 0x80; - public static final int FLAG_BIGRAM_FREQ = 0x7F; - - public static final int FOR_REVERSE_LOOKUPALL = -99; - - public ArrayList<String> mBigramToFill = new ArrayList<String>(); - public ArrayList<Integer> mBigramToFillAddress = new ArrayList<Integer>(); - - public HashMap<String, Bigram> mBi; - - public boolean mHasBigram; - - public BigramDictionary(String bigramSrcFilename, boolean hasBigram) { - mHasBigram = hasBigram; - loadBigram(bigramSrcFilename); - } - - private void loadBigram(String filename) { - mBi = new HashMap<String, Bigram>(); - if (!mHasBigram) { - System.out.println("Number of bigrams = " + Bigram.sBigramNum); - return; - } - try { - SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); - parser.parse(new File(filename), new DefaultHandler() { - String w1 = null; - boolean inWord1 = false; - boolean inWord2 = false; - int freq = 0, counter = 0; - Bigram tempBigram = null; - - @Override - public void startElement(String uri, String localName, - String qName, Attributes attributes) { - if (qName.equals("bi")) { - inWord1 = true; - w1 = attributes.getValue(0); - int count = Integer.parseInt(attributes.getValue(1)); - tempBigram = new Bigram(count); - counter = 0; - } else if (qName.equals("w")) { - inWord2 = true; - String word2 = attributes.getValue(0); - int freq = Integer.parseInt(attributes.getValue(1)); - tempBigram.setWord2(counter, word2, freq); - counter++; - Bigram.sBigramNum++; - } - } - - @Override - public void endElement(String uri, String localName, - String qName) { - if (inWord2) { - inWord2 = false; - } else if (inWord1) { - inWord1 = false; - mBi.put(w1, tempBigram); - } - } - }); - } catch (Exception ioe) { - System.err.println("Exception in parsing bigram\n" + ioe); - ioe.printStackTrace(); - } - System.out.println("Number of bigrams = " + Bigram.sBigramNum); - } - - byte[] writeBigrams(byte[] dict, Map<String, Integer> mDictionary) { - for (int i = 0; i < mBigramToFill.size(); i++) { - String w1 = mBigramToFill.get(i); - int address = mBigramToFillAddress.get(i); - - Bigram temp = mBi.get(w1); - int word2Count = temp.count; - int j4; - for (int j = 0; j < word2Count; j++) { - if (!mDictionary.containsKey(temp.word2[j])) { - System.out.println("Not in dictionary: " + temp.word2[j]); - System.exit(0); - } else { - j4 = (j * 4); - int addressOfWord2 = mDictionary.get(temp.word2[j]); - dict[address + j4 + 0] = (byte) (((addressOfWord2 & 0x3F0000) >> 16) - | FLAG_BIGRAM_READ); - dict[address + j4 + 1] = (byte) ((addressOfWord2 & 0x00FF00) >> 8); - dict[address + j4 + 2] = (byte) ((addressOfWord2 & 0x0000FF)); - - if (j == (word2Count - 1)) { - dict[address + j4 + 3] = (byte) (temp.freq[j] & FLAG_BIGRAM_FREQ); - } else { - dict[address + j4 + 3] = (byte) ((temp.freq[j] & FLAG_BIGRAM_FREQ) - | FLAG_BIGRAM_CONTINUED); - } - } - } - } - - return dict; - } - - void reverseLookupAll(Map<String, Integer> mDictionary, byte[] dict) { - Set<String> st = mDictionary.keySet(); - for (String s : st) { - searchForTerminalNode(mDictionary.get(s), FOR_REVERSE_LOOKUPALL, dict); - } - } - - void searchForTerminalNode(int bigramAddress, int frequency, byte[] dict) { - StringBuilder sb = new StringBuilder(48); - int pos; - boolean found = false; - int followDownBranchAddress = 2; - char followingChar = ' '; - int depth = 0; - int totalLoopCount = 0; - - while (!found) { - boolean followDownAddressSearchStop = false; - boolean firstAddress = true; - boolean haveToSearchAll = true; - - if (depth > 0) { - sb.append(followingChar); - } - pos = followDownBranchAddress; // pos start at count - int count = dict[pos] & 0xFF; - pos++; - for (int i = 0; i < count; i++) { - totalLoopCount++; - // pos at data - pos++; - // pos now at flag - if (!MakeBinaryDictionary.getFirstBitOfByte(pos, dict)) { // non-terminal - if (!followDownAddressSearchStop) { - int addr = MakeBinaryDictionary.get22BitAddress(pos, dict); - if (addr > bigramAddress) { - followDownAddressSearchStop = true; - if (firstAddress) { - firstAddress = false; - haveToSearchAll = true; - } else if (!haveToSearchAll) { - break; - } - } else { - followDownBranchAddress = addr; - followingChar = (char) (0xFF & dict[pos-1]); - if(firstAddress) { - firstAddress = false; - haveToSearchAll = false; - } - } - } - pos += 3; - } else if (MakeBinaryDictionary.getFirstBitOfByte(pos, dict)) { // terminal - // found !! - if (bigramAddress == (pos-1)) { - sb.append((char) (0xFF & dict[pos-1])); - found = true; - break; - } - - // address + freq (4 byte) - if (MakeBinaryDictionary.getSecondBitOfByte(pos, dict)) { - if (!followDownAddressSearchStop) { - int addr = MakeBinaryDictionary.get22BitAddress(pos, dict); - if (addr > bigramAddress) { - followDownAddressSearchStop = true; - if (firstAddress) { - firstAddress = false; - haveToSearchAll = true; - } else if (!haveToSearchAll) { - break; - } - } else { - followDownBranchAddress = addr; - followingChar = (char) (0xFF & dict[pos-1]); - if(firstAddress) { - firstAddress = false; - haveToSearchAll = true; - } - } - } - pos += 4; - } else { // freq only (2 byte) - pos += 2; - } - // skipping bigram - int bigramExist = (dict[pos] & FLAG_BIGRAM_READ); - if (bigramExist > 0) { - int nextBigramExist = 1; - while (nextBigramExist > 0) { - pos += 3; - nextBigramExist = (dict[pos++] & FLAG_BIGRAM_CONTINUED); - } - } else { - pos++; - } - } - } - depth++; - if (followDownBranchAddress == 2) { - System.out.println("ERROR!!! Cannot find bigram!!"); - System.exit(0); - } - } - - if (frequency == FOR_REVERSE_LOOKUPALL) { - System.out.println("Reverse: " + sb.toString() + " (" + bigramAddress + ")" - + " Loop: " + totalLoopCount); - } else { - System.out.println(" bigram: " + sb.toString() + " (" + bigramAddress + ") freq: " - + frequency + " Loop: " + totalLoopCount); - } - } - - static class Bigram { - String[] word2; - int[] freq; - int count; - static int sBigramNum = 0; - - String getSecondWord(int i) { - return word2[i]; - } - - int getFrequency(int i) { - return (freq[i] == 0) ? 1 : freq[i]; - } - - void setWord2(int index, String word2, int freq) { - this.word2[index] = word2; - this.freq[index] = freq; - } - - public Bigram(int word2Count) { - count = word2Count; - word2 = new String[word2Count]; - freq = new int[word2Count]; - } - } -} diff --git a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java deleted file mode 100644 index 4a285ff07..000000000 --- a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java +++ /dev/null @@ -1,445 +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.tools.dict; - -import org.xml.sax.Attributes; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Compresses a list of words, frequencies, and bigram data - * into a tree structured binary dictionary. - * Dictionary Version: 200 (may contain bigrams) - * Version number started from 200 rather than 1 because we wanted to prevent number of roots in - * any old dictionaries being mistaken as the version number. There is not a chance that there - * will be more than 200 roots. Version number should be increased when there is structural change - * in the data. There is no need to increase the version when only the words in the data changes. - */ -public class MakeBinaryDictionary { - private static final int VERSION_NUM = 200; - - private static final String TAG_WORD = "w"; - private static final String ATTR_FREQ = "f"; - - private static final int FLAG_ADDRESS_MASK = 0x400000; - private static final int FLAG_TERMINAL_MASK = 0x800000; - private static final int ADDRESS_MASK = 0x3FFFFF; - - private static final int INITIAL_STRING_BUILDER_CAPACITY = 48; - - /** - * Unit for this variable is in bytes - * If destination file name is main.dict and file limit causes dictionary to be separated into - * multiple file, it will generate main0.dict, main1.dict, and so forth. - */ - private static int sOutputFileSize; - private static boolean sSplitOutput; - - private static final CharNode EMPTY_NODE = new CharNode(); - - private List<CharNode> mRoots; - private Map<String, Integer> mDictionary; - private int mWordCount; - - private BigramDictionary mBigramDict; - - private static class CharNode { - char data; - int freq; - boolean terminal; - List<CharNode> children; - static int sNodes; - - public CharNode() { - sNodes++; - } - } - - private static void usage() { - System.err.println("Usage: makedict -s <src_dict.xml> [-b <src_bigram.xml>] " - + "-d <dest.dict> [--size filesize]"); - System.exit(-1); - } - - public static void main(String[] args) { - int checkSource = -1; - int checkBigram = -1; - int checkDest = -1; - int checkFileSize = -1; - for (int i = 0; i < args.length; i+=2) { - if (args[i].equals("-s")) checkSource = (i + 1); - if (args[i].equals("-b")) checkBigram = (i + 1); - if (args[i].equals("-d")) checkDest = (i + 1); - if (args[i].equals("--size")) checkFileSize = (i + 1); - } - if (checkFileSize >= 0) { - sSplitOutput = true; - sOutputFileSize = Integer.parseInt(args[checkFileSize]); - } else { - sSplitOutput = false; - } - if (checkDest >= 0 && !args[checkDest].endsWith(".dict")) { - System.err.println("Error: Dictionary output file extension should be \".dict\""); - usage(); - } else if (checkSource >= 0 && checkBigram >= 0 && checkDest >= 0 && - ((!sSplitOutput && args.length == 6) || (sSplitOutput && args.length == 8))) { - new MakeBinaryDictionary(args[checkSource], args[checkBigram], args[checkDest]); - } else if (checkSource >= 0 && checkDest >= 0 && - ((!sSplitOutput && args.length == 4) || (sSplitOutput && args.length == 6))) { - new MakeBinaryDictionary(args[checkSource], null, args[checkDest]); - } else { - usage(); - } - } - - private MakeBinaryDictionary(String srcFilename, String bigramSrcFilename, - String destFilename) { - System.out.println("Generating dictionary version " + VERSION_NUM); - mBigramDict = new BigramDictionary(bigramSrcFilename, (bigramSrcFilename != null)); - populateDictionary(srcFilename); - writeToDict(destFilename); - - // Enable the code below to verify that the generated tree is traversable - // and bigram data is stored correctly. - if (false) { - mBigramDict.reverseLookupAll(mDictionary, mDict); - traverseDict(2, new char[32], 0); - } - } - - private void populateDictionary(String filename) { - mRoots = new ArrayList<CharNode>(); - mDictionary = new HashMap<String, Integer>(); - try { - SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); - parser.parse(new File(filename), new DefaultHandler() { - boolean inWord; - int freq; - StringBuilder wordBuilder = new StringBuilder(INITIAL_STRING_BUILDER_CAPACITY); - - @Override - public void startElement(String uri, String localName, - String qName, Attributes attributes) { - if (qName.equals(TAG_WORD)) { - inWord = true; - freq = Integer.parseInt(attributes.getValue(ATTR_FREQ)); - wordBuilder.setLength(0); - } - } - - @Override - public void characters(char[] data, int offset, int length) { - // Ignore other whitespace - if (!inWord) return; - wordBuilder.append(data, offset, length); - } - - @Override - public void endElement(String uri, String localName, - String qName) { - if (qName.equals(TAG_WORD)) { - if (wordBuilder.length() >= 1) { - addWordTop(wordBuilder.toString(), freq); - mWordCount++; - } - inWord = false; - } - } - }); - } catch (Exception ioe) { - System.err.println("Exception in parsing\n" + ioe); - ioe.printStackTrace(); - } - System.out.println("Nodes = " + CharNode.sNodes); - } - - private static int indexOf(List<CharNode> children, char c) { - if (children == null) { - return -1; - } - for (int i = 0; i < children.size(); i++) { - if (children.get(i).data == c) { - return i; - } - } - return -1; - } - - private void addWordTop(String word, int freq) { - if (freq < 0) { - freq = 0; - } else if (freq > 255) { - freq = 255; - } - char firstChar = word.charAt(0); - int index = indexOf(mRoots, firstChar); - if (index == -1) { - CharNode newNode = new CharNode(); - newNode.data = firstChar; - index = mRoots.size(); - mRoots.add(newNode); - } - final CharNode node = mRoots.get(index); - if (word.length() > 1) { - addWordRec(node, word, 1, freq); - } else { - node.terminal = true; - node.freq = freq; - } - } - - private void addWordRec(CharNode parent, String word, int charAt, int freq) { - CharNode child = null; - char data = word.charAt(charAt); - if (parent.children == null) { - parent.children = new ArrayList<CharNode>(); - } else { - for (int i = 0; i < parent.children.size(); i++) { - CharNode node = parent.children.get(i); - if (node.data == data) { - child = node; - break; - } - } - } - if (child == null) { - child = new CharNode(); - parent.children.add(child); - } - child.data = data; - if (word.length() > charAt + 1) { - addWordRec(child, word, charAt + 1, freq); - } else { - child.terminal = true; - child.freq = freq; - } - } - - private byte[] mDict; - private int mDictSize; - private static final int CHAR_WIDTH = 8; - private static final int FLAGS_WIDTH = 1; // Terminal flag (word end) - private static final int ADDR_WIDTH = 23; // Offset to children - private static final int FREQ_WIDTH_BYTES = 1; - private static final int COUNT_WIDTH_BYTES = 1; - - private void addCount(int count) { - mDict[mDictSize++] = (byte) (0xFF & count); - } - - private void addNode(CharNode node, String word1) { - if (node.terminal) { // store address of each word1 for bigram dic generation - mDictionary.put(word1, mDictSize); - } - - int charData = 0xFFFF & node.data; - if (charData > 254) { - mDict[mDictSize++] = (byte) 255; - mDict[mDictSize++] = (byte) ((node.data >> 8) & 0xFF); - mDict[mDictSize++] = (byte) (node.data & 0xFF); - } else { - mDict[mDictSize++] = (byte) (0xFF & node.data); - } - if (node.children != null) { - mDictSize += 3; // Space for children address - } else { - mDictSize += 1; // Space for just the terminal/address flags - } - if ((0xFFFFFF & node.freq) > 255) { - node.freq = 255; - } - if (node.terminal) { - byte freq = (byte) (0xFF & node.freq); - mDict[mDictSize++] = freq; - // bigram - if (mBigramDict.mBi.containsKey(word1)) { - int count = mBigramDict.mBi.get(word1).count; - mBigramDict.mBigramToFill.add(word1); - mBigramDict.mBigramToFillAddress.add(mDictSize); - mDictSize += (4 * count); - } else { - mDict[mDictSize++] = (byte) (0x00); - } - } - } - - private int mNullChildrenCount = 0; - private int mNotTerminalCount = 0; - - private void updateNodeAddress(int nodeAddress, CharNode node, - int childrenAddress) { - if ((mDict[nodeAddress] & 0xFF) == 0xFF) { // 3 byte character - nodeAddress += 2; - } - childrenAddress = ADDRESS_MASK & childrenAddress; - if (childrenAddress == 0) { - mNullChildrenCount++; - } else { - childrenAddress |= FLAG_ADDRESS_MASK; - } - if (node.terminal) { - childrenAddress |= FLAG_TERMINAL_MASK; - } else { - mNotTerminalCount++; - } - mDict[nodeAddress + 1] = (byte) (childrenAddress >> 16); - if ((childrenAddress & FLAG_ADDRESS_MASK) != 0) { - mDict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8); - mDict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF)); - } - } - - private void writeWordsRec(List<CharNode> children, StringBuilder word) { - if (children == null || children.size() == 0) { - return; - } - final int childCount = children.size(); - addCount(childCount); - int[] childrenAddresses = new int[childCount]; - for (int j = 0; j < childCount; j++) { - CharNode child = children.get(j); - childrenAddresses[j] = mDictSize; - word.append(child.data); - addNode(child, word.toString()); - word.setLength(word.length() - 1); - } - for (int j = 0; j < childCount; j++) { - CharNode child = children.get(j); - int nodeAddress = childrenAddresses[j]; - int cacheDictSize = mDictSize; - word.append(child.data); - writeWordsRec(child.children, word); - word.setLength(word.length() - 1); - updateNodeAddress(nodeAddress, child, child.children != null ? cacheDictSize : 0); - } - } - - private void writeToDict(String dictFilename) { - // 4MB max, 22-bit offsets - mDict = new byte[4 * 1024 * 1024]; // 4MB upper limit. Actual is probably - // < 1MB in most cases, as there is a limit in the - // resource size in apks. - mDictSize = 0; - - mDict[mDictSize++] = (byte) (0xFF & VERSION_NUM); // version info - mDict[mDictSize++] = (byte) (0xFF & (mBigramDict.mHasBigram ? 1 : 0)); - - final StringBuilder word = new StringBuilder(INITIAL_STRING_BUILDER_CAPACITY); - writeWordsRec(mRoots, word); - mDict = mBigramDict.writeBigrams(mDict, mDictionary); - System.out.println("Dict Size = " + mDictSize); - if (!sSplitOutput) { - sOutputFileSize = mDictSize; - } - try { - int currentLoc = 0; - int i = 0; - int extension = dictFilename.indexOf(".dict"); - String filename = dictFilename.substring(0, extension); - while (mDictSize > 0) { - FileOutputStream fos; - if (sSplitOutput) { - fos = new FileOutputStream(filename + i + ".dict"); - } else { - fos = new FileOutputStream(filename + ".dict"); - } - if (mDictSize > sOutputFileSize) { - fos.write(mDict, currentLoc, sOutputFileSize); - mDictSize -= sOutputFileSize; - currentLoc += sOutputFileSize; - } else { - fos.write(mDict, currentLoc, mDictSize); - mDictSize = 0; - } - fos.close(); - i++; - } - } catch (IOException ioe) { - System.err.println("Error writing dict file:" + ioe); - } - } - - private void traverseDict(int pos, char[] word, int depth) { - int count = mDict[pos++] & 0xFF; - for (int i = 0; i < count; i++) { - char c = (char) (mDict[pos++] & 0xFF); - if (c == 0xFF) { // two byte character - c = (char) (((mDict[pos] & 0xFF) << 8) | (mDict[pos+1] & 0xFF)); - pos += 2; - } - word[depth] = c; - boolean terminal = getFirstBitOfByte(pos, mDict); - int address = 0; - if ((mDict[pos] & (FLAG_ADDRESS_MASK >> 16)) > 0) { // address check - address = get22BitAddress(pos, mDict); - pos += 3; - } else { - pos += 1; - } - if (terminal) { - showWord(word, depth + 1, mDict[pos] & 0xFF); - pos++; - - int bigramExist = (mDict[pos] & mBigramDict.FLAG_BIGRAM_READ); - if (bigramExist > 0) { - int nextBigramExist = 1; - while (nextBigramExist > 0) { - int bigramAddress = get22BitAddress(pos, mDict); - pos += 3; - int frequency = (mBigramDict.FLAG_BIGRAM_FREQ & mDict[pos]); - mBigramDict.searchForTerminalNode(bigramAddress, frequency, mDict); - nextBigramExist = (mDict[pos++] & mBigramDict.FLAG_BIGRAM_CONTINUED); - } - } else { - pos++; - } - } - if (address != 0) { - traverseDict(address, word, depth + 1); - } - } - } - - private static void showWord(char[] word, int size, int freq) { - System.out.print(new String(word, 0, size) + " " + freq + "\n"); - } - - /* package */ static int get22BitAddress(int pos, byte[] dict) { - return ((dict[pos + 0] & 0x3F) << 16) - | ((dict[pos + 1] & 0xFF) << 8) - | ((dict[pos + 2] & 0xFF)); - } - - /* package */ static boolean getFirstBitOfByte(int pos, byte[] dict) { - return (dict[pos] & 0x80) > 0; - } - - /* package */ static boolean getSecondBitOfByte(int pos, byte[] dict) { - return (dict[pos] & 0x40) > 0; - } -} diff --git a/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java b/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java new file mode 100644 index 000000000..79cf14b2b --- /dev/null +++ b/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java @@ -0,0 +1,55 @@ +/* + * 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 com.android.inputmethod.latin.FusionDictionary.Node; + +import java.util.ArrayList; + +import junit.framework.TestCase; + +/** + * Unit tests for BinaryDictInputOutput. + */ +public class BinaryDictInputOutputTest extends TestCase { + + public void setUp() throws Exception { + super.setUp(); + } + + public void tearDown() throws Exception { + super.tearDown(); + } + + // Test the flattened array contains the expected number of nodes, and + // that it does not contain any duplicates. + public void testFlattenNodes() { + final FusionDictionary dict = new FusionDictionary(); + dict.add("foo", 1, null); + dict.add("fta", 1, null); + dict.add("ftb", 1, null); + dict.add("bar", 1, null); + dict.add("fool", 1, null); + final ArrayList<Node> result = BinaryDictInputOutput.flattenTree(dict.mRoot); + assertEquals(4, result.size()); + while (!result.isEmpty()) { + final Node n = result.remove(0); + assertFalse("Flattened array contained the same node twice", result.contains(n)); + } + } + +} |