diff options
Diffstat (limited to 'java')
101 files changed, 3359 insertions, 1994 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index 8ffa29d71..5f64768d4 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -27,7 +27,6 @@ <application android:label="@string/aosp_android_keyboard_ime_name" android:icon="@mipmap/ic_ime_settings" - android:backupAgent="BackupAgent" android:killAfterRestore="false" android:supportsRtl="true"> diff --git a/java/proguard.flags b/java/proguard.flags index ac5b7df16..d65924f7c 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -1,64 +1,11 @@ --keep class com.android.inputmethod.latin.BinaryDictionary { - int mDictLength; - <init>(...); +# Keep classes and methods that have the @UsedForTesting annotation +-keep @com.android.inputmethod.annotations.UsedForTesting class * +-keepclassmembers class * { +@com.android.inputmethod.annotations.UsedForTesting *; } --keep class com.android.inputmethod.keyboard.ProximityInfo { - <init>(com.android.inputmethod.keyboard.ProximityInfo); +# Keep classes and methods that have the @ExternallyReferenced annotation +-keep @com.android.inputmethod.annotations.ExternallyReferenced class * +-keepclassmembers class * { +@com.android.inputmethod.annotations.ExternallyReferenced *; } - --keep class com.android.inputmethod.latin.Suggest { - <init>(...); - com.android.inputmethod.latin.SuggestedWords getSuggestions(...); -} - --keep class com.android.inputmethod.latin.AutoCorrection { - java.lang.CharSequence getAutoCorrectionWord(); -} - --keep class com.android.inputmethod.latin.Utils { - boolean equalsIgnoreCase(...); -} - --keep class com.android.inputmethod.latin.InputPointers { - *; -} - --keep class com.android.inputmethod.latin.ResizableIntArray { - *; -} - --keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment { - *; -} - --keep class com.android.inputmethod.keyboard.MainKeyboardView { - # Keep getter/setter methods for ObjectAnimator - int getLanguageOnSpacebarAnimAlpha(); - void setLanguageOnSpacebarAnimAlpha(int); - int getAltCodeKeyWhileTypingAnimAlpha(); - void setAltCodeKeyWhileTypingAnimAlpha(int); -} - --keep class com.android.inputmethod.keyboard.MoreKeysKeyboard$Builder$MoreKeysKeyboardParams { - <init>(...); -} - --keepclasseswithmembernames class * { - native <methods>; -} - --keep class com.android.inputmethod.research.ResearchLogger { - void flush(); - void publishCurrentLogUnit(...); -} - --keep class com.android.inputmethod.keyboard.KeyboardLayoutSet$Builder { - void setTouchPositionCorrectionEnabled(...); -} - -# The support library contains references to newer platform versions. -# Don't warn about those in case this app is linking against an older -# platform version. We know about them, and they are safe. --dontwarn android.support.v4.** --dontwarn android.support.v13.** diff --git a/java/res/layout/suggestion_word.xml b/java/res/layout/suggestion_word.xml index d64cacf04..fa00e041e 100644 --- a/java/res/layout/suggestion_word.xml +++ b/java/res/layout/suggestion_word.xml @@ -18,6 +18,8 @@ */ --> +<!-- Provide a haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's haptic feedback settings. --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" @@ -29,6 +31,7 @@ android:paddingTop="0dp" android:paddingRight="@dimen/suggestion_padding" android:paddingBottom="0dp" + android:hapticFeedbackEnabled="false" android:focusable="false" android:clickable="false" android:singleLine="true" diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index 897bfb712..288e37b06 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -69,7 +69,7 @@ <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: gespeichert"</string> <string name="label_go_key" msgid="1635148082137219148">"Los"</string> <string name="label_next_key" msgid="362972844525672568">"Weiter"</string> - <string name="label_previous_key" msgid="1211868118071386787">"Vorh."</string> + <string name="label_previous_key" msgid="1211868118071386787">"Zurück"</string> <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> @@ -121,7 +121,7 @@ <string name="prefs_enable_log" msgid="6620424505072963557">"Nutzer-Feedback aktivieren"</string> <string name="prefs_description_log" msgid="5827825607258246003">"Tragen Sie zur Verbesserung dieses Eingabemethodeneditors bei, indem Sie automatisch Nutzungsstatistiken und Absturzberichte an Google senden."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturdesign"</string> - <string name="subtype_en_GB" msgid="88170601942311355">"Englisch (Großbritannien)"</string> + <string name="subtype_en_GB" msgid="88170601942311355">"Englisch (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Englisch (USA)"</string> <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Englisch (GB) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string> <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Englisch (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 00e574e90..ed998a891 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -37,7 +37,7 @@ <string name="advanced_settings_summary" msgid="4487980456152830271">"Для опытных пользователей"</string> <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Другой способ ввода"</string> <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавиша переключения языков также служит для смены способа ввода"</string> - <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиша переключения языков"</string> + <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиша смены языка"</string> <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показывать, когда включено несколько раскладок"</string> <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Задержка закрытия"</string> <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задержки"</string> @@ -59,7 +59,7 @@ <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умеренное"</string> <string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активное"</string> <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Очень активно"</string> - <string name="bigram_prediction" msgid="1084449187723948550">"Подсказка следующего слова"</string> + <string name="bigram_prediction" msgid="1084449187723948550">"Подсказывать слова"</string> <string name="bigram_prediction_summary" msgid="3896362682751109677">"Предлагать подсказки на основе предыдущего слова"</string> <string name="gesture_input" msgid="826951152254563827">"Включить функцию"</string> <string name="gesture_input_summary" msgid="9180350639305731231">"Вводите слова, не отрывая пальца от клавиатуры"</string> @@ -106,7 +106,7 @@ <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Режим ввода текста"</string> <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Режим набора номера"</string> <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Режим телефонных символов"</string> - <string name="voice_input" msgid="3583258583521397548">"Ключ голосового ввода"</string> + <string name="voice_input" msgid="3583258583521397548">"Кнопка голосового ввода"</string> <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Значок на основной клавиатуре"</string> <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Значок на клавиатуре символов"</string> <string name="voice_input_modes_off" msgid="3745699748218082014">"Выкл."</string> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 9e07b2248..8db436bef 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -140,10 +140,12 @@ <string-array name="subtype_locale_exception_keys"> <item>en_US</item> <item>en_GB</item> + <item>es_US</item> </string-array> <string-array name="subtype_locale_exception_values"> <item>English (US)</item> <item>English (UK)</item> + <item>Español (EE.UU.)</item> </string-array> <!-- Generic subtype label --> diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index dbb56ab4d..4766a2295 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -124,6 +124,7 @@ <item name="keyboardTopPadding">0dp</item> <item name="keyboardBottomPadding">0dp</item> <item name="horizontalGap">0dp</item> + <item name="touchPositionCorrectionData">@null</item> </style> <style name="MoreKeysKeyboardView" @@ -231,6 +232,7 @@ <item name="keyboardTopPadding">0dp</item> <item name="keyboardBottomPadding">0dp</item> <item name="horizontalGap">0dp</item> + <item name="touchPositionCorrectionData">@null</item> </style> <style name="MoreKeysKeyboardView.Stone" @@ -300,6 +302,7 @@ <item name="keyboardTopPadding">0dp</item> <item name="keyboardBottomPadding">0dp</item> <item name="horizontalGap">0dp</item> + <item name="touchPositionCorrectionData">@null</item> </style> <style name="MoreKeysKeyboardView.Gingerbread" @@ -355,6 +358,7 @@ <item name="keyboardTopPadding">0dp</item> <item name="keyboardBottomPadding">0dp</item> <item name="horizontalGap">0dp</item> + <item name="touchPositionCorrectionData">@null</item> </style> <style name="MoreKeysKeyboardView.IceCreamSandwich" diff --git a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml index d486b9df0..b11bbba1b 100644 --- a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml +++ b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml @@ -52,8 +52,8 @@ latin:keyStyle="hasShiftedLetterHintStyle" /> <Key latin:keyLabel="3" - latin:keyHintLabel="#" - latin:additionalMoreKeys="#" + latin:keyHintLabel="\#" + latin:additionalMoreKeys="\#" latin:moreKeys="!text/more_keys_for_symbols_3" latin:keyStyle="hasShiftedLetterHintStyle" /> <Key diff --git a/java/res/xml-sw600dp/rowkeys_symbols2.xml b/java/res/xml-sw600dp/rowkeys_symbols2.xml index d7067e0cf..7d7dcfe31 100644 --- a/java/res/xml-sw600dp/rowkeys_symbols2.xml +++ b/java/res/xml-sw600dp/rowkeys_symbols2.xml @@ -34,7 +34,7 @@ </case> <default> <Key - latin:keyLabel="#" /> + latin:keyLabel="\#" /> </default> </switch> <Key @@ -49,9 +49,9 @@ <!-- U+066B: "٫" ARABIC DECIMAL SEPARATOR --> <Key latin:keyLabel="٫" - latin:keyHintLabel="#" + latin:keyHintLabel="\#" latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint" - latin:moreKeys="#" /> + latin:moreKeys="\#" /> </case> <default> <Key diff --git a/java/res/xml-sw600dp/rows_mongolian.xml b/java/res/xml-sw600dp/rows_mongolian.xml new file mode 100644 index 000000000..cfd8c8c39 --- /dev/null +++ b/java/res/xml-sw600dp/rows_mongolian.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="8.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_mongolian1" + latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="8.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_mongolian2" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="8.182%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_mongolian3" /> + <include + latin:keyboardLayout="@xml/keys_comma_period" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml index f69239456..37bf2e808 100644 --- a/java/res/xml-sw600dp/rows_number_normal.xml +++ b/java/res/xml-sw600dp/rows_number_normal.xml @@ -151,7 +151,7 @@ latin:keyLabel="0" latin:keyStyle="numKeyStyle" /> <Key - latin:keyLabel="#" + latin:keyLabel="\#" latin:keyStyle="numKeyStyle" /> <!-- Note: This Spacer prevents the above key from being marked as a right edge key. --> <Spacer diff --git a/java/res/xml-sw600dp/rows_phone.xml b/java/res/xml-sw600dp/rows_phone.xml index dcc4fde7d..c4799bbcd 100644 --- a/java/res/xml-sw600dp/rows_phone.xml +++ b/java/res/xml-sw600dp/rows_phone.xml @@ -116,7 +116,7 @@ <Key latin:keyStyle="num0KeyStyle" /> <Key - latin:keyLabel="#" + latin:keyLabel="\#" latin:keyStyle="numKeyStyle" /> </Row> </merge> diff --git a/java/res/xml-sw768dp/rows_mongolian.xml b/java/res/xml-sw768dp/rows_mongolian.xml new file mode 100644 index 000000000..9afd4e2db --- /dev/null +++ b/java/res/xml-sw768dp/rows_mongolian.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="7.375%p" + > + <Key + latin:keyStyle="tabKeyStyle" + latin:keyLabelFlags="alignLeft" + latin:keyWidth="7.969%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_mongolian1" + latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="7.227%p" + > + <Key + latin:keyStyle="toSymbolKeyStyle" + latin:keyLabelFlags="alignLeft" + latin:keyWidth="11.172%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_mongolian2" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="7.000%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="13.829%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_mongolian3" /> + <include + latin:keyboardLayout="@xml/keys_comma_period" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw768dp/rows_number_normal.xml b/java/res/xml-sw768dp/rows_number_normal.xml index d4d7c722a..de49aba91 100644 --- a/java/res/xml-sw768dp/rows_number_normal.xml +++ b/java/res/xml-sw768dp/rows_number_normal.xml @@ -166,7 +166,7 @@ latin:keyLabel="0" latin:keyStyle="numKeyStyle" /> <Key - latin:keyLabel="#" + latin:keyLabel="\#" latin:keyStyle="numKeyStyle" /> <!-- Note: This Spacer prevents the above key from being marked as a right edge key. --> <Spacer diff --git a/java/res/xml-sw768dp/rows_phone.xml b/java/res/xml-sw768dp/rows_phone.xml index 2f718db62..d06a63b2c 100644 --- a/java/res/xml-sw768dp/rows_phone.xml +++ b/java/res/xml-sw768dp/rows_phone.xml @@ -132,7 +132,7 @@ <Key latin:keyStyle="num0KeyStyle" /> <Key - latin:keyLabel="#" + latin:keyLabel="\#" latin:keyStyle="numKeyStyle" /> </Row> </merge> diff --git a/java/res/xml/kbd_mongolian.xml b/java/res/xml/kbd_mongolian.xml new file mode 100644 index 000000000..1d0352175 --- /dev/null +++ b/java/res/xml/kbd_mongolian.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"): +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/rows_mongolian" /> +</Keyboard> diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml index 6dea16f89..76fe0e6b0 100644 --- a/java/res/xml/key_styles_currency.xml +++ b/java/res/xml/key_styles_currency.xml @@ -28,7 +28,7 @@ <include latin:keyboardLayout="@xml/key_styles_currency_dollar" /> </case> - <!-- Countries using Euro currency, 23 countries as for January 2011. --> + <!-- Countries using Euro currency, 23 countries as of November 2012. 1. Andorra (ca_AD, ca_ES) 2. Austria (de_AT) 3. Belgium (nl_BE, fr_BE, de_BE) @@ -40,80 +40,77 @@ 9. Greece (el_GR) 10. Ireland (ga_IE, en_IE) 11. Italy (it_IT) - 12. Kosovo (??_XK) ?? + 12. Kosovo (sq_XK, sr_XK) 13. Luxembourg (lb_LU, fr_LU, de_LU) 14. Malta (mt_MT, en_MT) 15. Monaco (fr_MO) - 16. Montenegro (sla_ME) + 16. Montenegro (??_ME) 17. Netherlands (nl_NL) 18. Portugal (pt_PT) 19. San Marino (it_SM) 20. Slovakia (sk_SK) 21. Slovenia (sl_SI) 22. Spain (es_ES, ca_ES) - 23. Vatican City (it_VA) - --> - <!-- Though Denmark and Turkey don't using Euro as their currencies, but having Euro sign on - the symbol keyboard might be useful. Especially Danish krone (kr) and Turkish lira - (TL) can be represented by usual alphabet letters. --> - <!-- Note: Some locales may not have country code, and it it supposed to indicate the - country where the language originally/mainly spoken. --> + 23. Vatican City (it_VA) --> <case - latin:localeCode="da|de|es|el|fi|fr|it|nl|sk|sl|pt_PT|tr" + latin:countryCode="AD|AT|BE|CY|EE|FI|FR|DE|GR|IE|IT|XK|LU|MT|MO|ME|NL|PT|SM|SK|SI|ES|VA" > <include latin:keyboardLayout="@xml/key_styles_currency_euro" /> </case> + <!-- Note: Some subtype locale may not have country code, and it it supposed to indicate the + country where the language originally/mainly spoken. --> + <!-- Though Denmark, Sweden and Turkey don't use Euro as their currency, having the Euro + sign on the symbol keyboard might be useful. Especially Danish krone (kr), Swedish + krona (kr) and Turkish lira (TL) can be represented by usual alphabet letters. --> + <!-- TODO: The currency sign of Turkish Lira was created in 2012 and assigned U+20BA for + its unicode, although there is no font glyph for it as of November 2012. --> + <!-- da: Denmark (da_DK) + de: Germany (de_DE) + es: Spain (es_ES) + fi: Finland (fi_FI) + fr: France(fr_FR) + it: Italy (it_IT) + nl: Netherlands (nl_NL) + sk: Slovakia (sk_SK) + sl: Slovenia (sl_SL) + sv: Sweden (sv_SV) + tr: Trukey (tr_TR) --> <case - latin:languageCode="ca|et|lb|mt|sla" + latin:localeCode="da|de|es|el|fi|fr|it|nl|sk|sl|sv|tr" > <include latin:keyboardLayout="@xml/key_styles_currency_euro" /> </case> + <!-- ca: Catalan (Andorra, Spain) + et: Estonian (Estonia) + lb: Luxembougish (Luxembourg) + mt: Maltese (Malta) --> <case - latin:countryCode="AD|AT|BE|CY|EE|FI|FR|DE|GR|IE|IT|XK|LU|MT|MO|ME|NL|PT|SM|SK|SI|ES|VA" + latin:languageCode="ca|et|lb|mt" > <include latin:keyboardLayout="@xml/key_styles_currency_euro" /> </case> + <!-- fa: Persian (Rial and Afgahni) + hi: Hindi (Indian Rupee) + iw: Hebrew (New Sheqel) + mn: Mongolian (Tugrik) + th: Thai (Baht) + uk: Ukrainian (Hryvnia) + vi: Vietnamese (Dong) --> + <!-- TODO: The currency sign of Turkish Lira was created in 2012 and assigned U+20BA for + its unicode, although there is no font glyph for it as of November 2012. --> <case - latin:languageCode="iw" - > - <!-- U+20AA: "₪" NEW SHEQEL SIGN - U+00A3: "£" POUND SIGN - U+20AC: "€" EURO SIGN - U+00A2: "¢" CENT SIGN --> - <key-style - latin:styleName="currencyKeyStyle" - latin:keyLabel="₪" - latin:moreKeys="!text/more_keys_for_currency_general" /> - <key-style - latin:styleName="moreCurrency1KeyStyle" - latin:keyLabel="£" /> - <key-style - latin:styleName="moreCurrency2KeyStyle" - latin:keyLabel="€" /> - <key-style - latin:styleName="moreCurrency3KeyStyle" - latin:keyLabel="$" - latin:moreKeys="¢" /> - <key-style - latin:styleName="moreCurrency4KeyStyle" - latin:keyLabel="¢" /> - </case> - <case - latin:languageCode="fa" + latin:languageCode="fa|hi|iw|mn|th|uk|vi" > - <!-- U+FDFC: "﷼" RIAL SIGN - U+060B: "؋" AFGHANI SIGN - U+00A3: "£" POUND SIGN + <!-- U+00A3: "£" POUND SIGN U+20AC: "€" EURO SIGN U+00A2: "¢" CENT SIGN --> - <!-- TODO: DroidSansArabic lacks the glyph of U+FCDC: RIAL SIGN --> <key-style latin:styleName="currencyKeyStyle" - latin:keyLabel="﷼" - latin:moreKeys="!text/more_keys_for_currency_general,؋" /> + latin:keyLabel="!text/keylabel_for_currency_generic" + latin:moreKeys="!text/more_keys_for_currency_generic" /> <key-style latin:styleName="moreCurrency1KeyStyle" latin:keyLabel="£" /> @@ -128,18 +125,19 @@ latin:styleName="moreCurrency4KeyStyle" latin:keyLabel="¢" /> </case> - <!-- United Kingdom --> + <!-- GB: United Kingdom (Pound) --> <case latin:countryCode="GB" > <!-- U+00A3: "£" POUND SIGN U+20AC: "€" EURO SIGN U+00A5: "¥" YEN SIGN - U+00A2: "¢" CENT SIGN --> + U+00A2: "¢" CENT SIGN + U+20B1: "₱" PESO SIGN --> <key-style latin:styleName="currencyKeyStyle" latin:keyLabel="£" - latin:moreKeys="!text/more_keys_for_currency_pound" /> + latin:moreKeys="¢,$,€,¥,₱" /> <key-style latin:styleName="moreCurrency1KeyStyle" latin:keyLabel="€" /> @@ -154,6 +152,7 @@ latin:styleName="moreCurrency4KeyStyle" latin:keyLabel="¢" /> </case> + <!-- ar: Arabic (Dollar and Rial) --> <default> <include latin:keyboardLayout="@xml/key_styles_currency_dollar" /> diff --git a/java/res/xml/key_styles_currency_euro.xml b/java/res/xml/key_styles_currency_euro.xml index 686fb3033..c1b5e0384 100644 --- a/java/res/xml/key_styles_currency_euro.xml +++ b/java/res/xml/key_styles_currency_euro.xml @@ -20,13 +20,14 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> <!-- U+20AC: "€" EURO SIGN + U+00A2: "¢" CENT SIGN U+00A3: "£" POUND SIGN U+00A5: "¥" YEN SIGN - U+00A2: "¢" CENT SIGN --> + U+20B1: "₱" PESO SIGN --> <key-style latin:styleName="currencyKeyStyle" latin:keyLabel="€" - latin:moreKeys="!text/more_keys_for_currency_euro" /> + latin:moreKeys="¢,£,$,¥,₱" /> <key-style latin:styleName="moreCurrency1KeyStyle" latin:keyLabel="£" /> diff --git a/java/res/xml/keyboard_layout_set_mongolian.xml b/java/res/xml/keyboard_layout_set_mongolian.xml new file mode 100644 index 000000000..2d364f682 --- /dev/null +++ b/java/res/xml/keyboard_layout_set_mongolian.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardLayoutSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_mongolian" + latin:enableProximityCharsCorrection="true" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneSymbols" + latin:elementKeyboard="@xml/kbd_phone_symbols" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardLayoutSet> diff --git a/java/res/xml/keys_pcqwerty_symbols1.xml b/java/res/xml/keys_pcqwerty_symbols1.xml index bf48b1f17..2364e1087 100644 --- a/java/res/xml/keys_pcqwerty_symbols1.xml +++ b/java/res/xml/keys_pcqwerty_symbols1.xml @@ -32,7 +32,7 @@ <Key latin:keyLabel="\@" /> <Key - latin:keyLabel="#" /> + latin:keyLabel="\#" /> <Key latin:keyLabel="$" /> <!-- U+2030: "‰" PER MILLE SIGN --> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 038707134..aa59c577c 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -37,6 +37,7 @@ eo: Esperanto/spanish es: Spanish/spanish es_US: Spanish United States/spanish + (es_419: Spanish Latin America/qwerty) et: Estonian/nordic fa: Persian/arabic fi: Finnish/nordic @@ -54,6 +55,7 @@ lt: Lithuanian/qwerty lv: Latvian/qwerty mk: Macedonian/south_slavic + mn: Mongolian/mongolian ms: Malay/qwerty nb: Norwegian Bokmål/nordic nl: Dutch/qwerty @@ -87,161 +89,170 @@ android:isDefault="@bool/im_is_default"> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_en_US" - android:subtypeId="-921088104" + android:subtypeId="0xc9194f98" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_en_GB" - android:subtypeId="-1337596075" + android:subtypeId="0xb045e755" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1872175968" + android:subtypeId="0x6f972360" android:imeSubtypeLocale="af" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1494081088" + android:subtypeId="0x590dde40" android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="499361881" + android:subtypeId="0x1dc3a859" android:imeSubtypeLocale="be" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="195674344" + android:subtypeId="0x0ba9c0e8" android:imeSubtypeLocale="bg" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_bulgarian_bds" - android:subtypeId="1599191706" + android:subtypeId="0x5f51ba9a" android:imeSubtypeLocale="bg" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-756735787" + android:subtypeId="0xd2e520d5" android:imeSubtypeLocale="ca" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="758984400" + android:subtypeId="0x2d3d2ed0" android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="770990173" + android:subtypeId="0x2df4605d" android:imeSubtypeLocale="da" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="774684257" + android:subtypeId="0x2e2cbe61" android:imeSubtypeLocale="de" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="242746067" + android:subtypeId="0x0e7802d3" android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=greek" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1083200842" + android:subtypeId="0x4090554a" android:imeSubtypeLocale="eo" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="816242702" + android:subtypeId="0x30a6e00e" android:imeSubtypeLocale="es" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-2066550842" + android:subtypeId="0x84d2efc6" android:imeSubtypeLocale="es_US" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" /> + <!-- <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-332580523" + android:subtypeId="0x623f9286" + android:imeSubtypeLocale="es_419" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + /> + --> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:subtypeId="0xec2d3955" android:imeSubtypeLocale="et" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1100561836" + android:subtypeId="0xbe66c254" android:imeSubtypeLocale="fa" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="835636643" + android:subtypeId="0x31cecda3" android:imeSubtypeLocale="fi" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="843948332" + android:subtypeId="0x324da12c" android:imeSubtypeLocale="fr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-354699631" + android:subtypeId="0xeadbb691" android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="963984255" + android:subtypeId="0x39753b7f" android:imeSubtypeLocale="hi" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="901206634" + android:subtypeId="0x35b7526a" android:imeSubtypeLocale="hr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="903977197" + android:subtypeId="0x35e198ed" android:imeSubtypeLocale="hu" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -249,21 +260,21 @@ <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="2108597344" + android:subtypeId="0x7daea460" android:imeSubtypeLocale="in" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="2113214949" + android:subtypeId="0x7df519e5" android:imeSubtypeLocale="is" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="931682827" + android:subtypeId="0x37885a0b" android:imeSubtypeLocale="it" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -271,126 +282,133 @@ <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1727731901" + android:subtypeId="0x66fb18bd" android:imeSubtypeLocale="iw" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1846648426" + android:subtypeId="0x6e119e6a" android:imeSubtypeLocale="ka" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="775494660" + android:subtypeId="0x2e391c04" android:imeSubtypeLocale="ky" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-2094941373" + android:subtypeId="0x8321bb43" android:imeSubtypeLocale="lt" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-2093094331" + android:subtypeId="0x833dea45" android:imeSubtypeLocale="lv" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1353667716" + android:subtypeId="0xaf50ab7c" android:imeSubtypeLocale="mk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-2067235743" + android:subtypeId="0xcdcfc3ab" + android:imeSubtypeLocale="mn" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:subtypeId="0x84c87c61" android:imeSubtypeLocale="ms" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1058205204" + android:subtypeId="0x3f12ee14" android:imeSubtypeLocale="nb" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1067440414" + android:subtypeId="0x3f9fd91e" android:imeSubtypeLocale="nl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1343007020" + android:subtypeId="0x500ca92c" android:imeSubtypeLocale="nl_BE" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1124698716" + android:subtypeId="0x43098a5c" android:imeSubtypeLocale="pl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-889195354" + android:subtypeId="0xcafff4a6" android:imeSubtypeLocale="pt_BR" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-486540198" + android:subtypeId="0xe2fffc5a" android:imeSubtypeLocale="pt_PT" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1927784072" + android:subtypeId="0x8d185978" android:imeSubtypeLocale="ro" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1983547218" + android:subtypeId="0x763a8752" android:imeSubtypeLocale="ru" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1902849005" + android:subtypeId="0x8e94d413" android:imeSubtypeLocale="sk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1901925484" + android:subtypeId="0x8ea2eb94" android:imeSubtypeLocale="sl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="2009405806" + android:subtypeId="0x77c5196e" android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" @@ -398,14 +416,14 @@ <!-- TODO: Uncomment once we can handle IETF language tag with script name specified. <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_serbian_cyrillic" - android:subtypeId="XXXXXX" + android:subtypeId="0xXXXXXXXX" android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_serbian_latin" - android:subtypeId="XXXXXX" + android:subtypeId="0xXXXXXXXX" android:imeSubtypeLocale="sr-Latn" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" @@ -413,63 +431,63 @@ --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1219821379" + android:subtypeId="0x48b4ff43" android:imeSubtypeLocale="sv" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1891766753" + android:subtypeId="0x8f3dee1f" android:imeSubtypeLocale="sw" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="529847764" + android:subtypeId="0x1f94d5d4" android:imeSubtypeLocale="th" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=thai" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-259881489" + android:subtypeId="0xf08285ef" android:imeSubtypeLocale="tl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1244756446" + android:subtypeId="0x4a3179de" android:imeSubtypeLocale="tr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="1048856876" + android:subtypeId="0x3e84492c" android:imeSubtypeLocale="uk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1818808594" + android:subtypeId="0x93972eee" android:imeSubtypeLocale="vi" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" - android:subtypeId="-1693209738" + android:subtypeId="0x9b13ab76" android:imeSubtypeLocale="zu" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_no_language_qwerty" - android:subtypeId="-1573262419" + android:subtypeId="0xa239ebad" android:imeSubtypeLocale="zz" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable" diff --git a/java/res/xml/rowkeys_mongolian1.xml b/java/res/xml/rowkeys_mongolian1.xml new file mode 100644 index 000000000..6c8c8e2fd --- /dev/null +++ b/java/res/xml/rowkeys_mongolian1.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+0444: "ф" CYRILLIC SMALL LETTER EF --> + <Key + latin:keyLabel="ф" + latin:keyHintLabel="1" + latin:additionalMoreKeys="1" /> + <!-- U+0446: "ц" CYRILLIC SMALL LETTER TSE --> + <Key + latin:keyLabel="ц" + latin:keyHintLabel="2" + latin:additionalMoreKeys="2" /> + <!-- U+0443: "у" CYRILLIC SMALL LETTER U --> + <Key + latin:keyLabel="у" + latin:keyHintLabel="3" + latin:additionalMoreKeys="3" + latin:moreKeys="!text/more_keys_for_cyrillic_u" /> + <!-- U+0436: "ж" CYRILLIC SMALL LETTER ZHE --> + <Key + latin:keyLabel="ж" + latin:keyHintLabel="4" + latin:additionalMoreKeys="4" /> + <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> + <Key + latin:keyLabel="э" + latin:keyHintLabel="5" + latin:additionalMoreKeys="5" + latin:moreKeys="!text/more_keys_for_cyrillic_ie" /> + <!-- U+043D: "н" CYRILLIC SMALL LETTER EN --> + <Key + latin:keyLabel="н" + latin:keyHintLabel="6" + latin:additionalMoreKeys="6" + latin:moreKeys="!text/more_keys_for_cyrillic_en" /> + <!-- U+0433: "г" CYRILLIC SMALL LETTER GHE --> + <Key + latin:keyLabel="г" + latin:keyHintLabel="7" + latin:additionalMoreKeys="7" + latin:moreKeys="!text/more_keys_for_cyrillic_ghe" /> + <!-- U+0448: "ш" CYRILLIC SMALL LETTER SHA + U+0449: "щ" CYRILLIC SMALL LETTER SHCHA --> + <Key + latin:keyLabel="ш" + latin:keyHintLabel="8" + latin:additionalMoreKeys="8" + latin:moreKeys="щ" /> + <!-- U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U --> + <Key + latin:keyLabel="ү" + latin:keyHintLabel="9" + latin:additionalMoreKeys="9" /> + <!-- U+0437: "з" CYRILLIC SMALL LETTER ZE --> + <Key + latin:keyLabel="з" + latin:keyHintLabel="0" + latin:additionalMoreKeys="0" /> + <!-- U+043A: "к" CYRILLIC SMALL LETTER KA --> + <Key + latin:keyLabel="к" /> +</merge> diff --git a/java/res/xml/rowkeys_mongolian2.xml b/java/res/xml/rowkeys_mongolian2.xml new file mode 100644 index 000000000..a8aa00620 --- /dev/null +++ b/java/res/xml/rowkeys_mongolian2.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+0439: "й" CYRILLIC SMALL LETTER SHORT I --> + <Key + latin:keyLabel="й" /> + <!-- U+044B: "ы" CYRILLIC SMALL LETTER YERU --> + <Key + latin:keyLabel="ы" /> + <!-- U+0431: "б" CYRILLIC SMALL LETTER BE --> + <Key + latin:keyLabel="б" /> + <!-- U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O --> + <Key + latin:keyLabel="ө" /> + <!-- U+0430: "а" CYRILLIC SMALL LETTER A --> + <Key + latin:keyLabel="а" /> + <!-- U+0445: "х" CYRILLIC SMALL LETTER HA --> + <Key + latin:keyLabel="х" /> + <!-- U+0440: "р" CYRILLIC SMALL LETTER ER --> + <Key + latin:keyLabel="р" /> + <!-- U+043E: "о" CYRILLIC SMALL LETTER O --> + <Key + latin:keyLabel="о" /> + <!-- U+043B: "л" CYRILLIC SMALL LETTER EL --> + <Key + latin:keyLabel="л" /> + <!-- U+0434: "д" CYRILLIC SMALL LETTER DE --> + <Key + latin:keyLabel="д" /> + <!-- U+043F: "п" CYRILLIC SMALL LETTER PE --> + <Key + latin:keyLabel="п" /> +</merge> diff --git a/java/res/xml/rowkeys_mongolian3.xml b/java/res/xml/rowkeys_mongolian3.xml new file mode 100644 index 000000000..dc80c37ab --- /dev/null +++ b/java/res/xml/rowkeys_mongolian3.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- U+044F: "я" CYRILLIC SMALL LETTER YA --> + <Key + latin:keyLabel="я" /> + <!-- U+0447: "ч" CYRILLIC SMALL LETTER CHE --> + <Key + latin:keyLabel="ч" /> + <!-- U+0451: "ё" CYRILLIC SMALL LETTER IO + U+0435: "е" CYRILLIC SMALL LETTER IE --> + <Key + latin:keyLabel="ё" + latin:moreKeys="е" /> + <!-- U+0441: "с" CYRILLIC SMALL LETTER ES --> + <Key + latin:keyLabel="с" /> + <!-- U+043C: "м" CYRILLIC SMALL LETTER EM --> + <Key + latin:keyLabel="м" /> + <!-- U+0438: "и" CYRILLIC SMALL LETTER I --> + <Key + latin:keyLabel="и" /> + <!-- U+0442: "т" CYRILLIC SMALL LETTER TE --> + <Key + latin:keyLabel="т" /> + <!-- U+044C: "ь" CYRILLIC SMALL LETTER SOFT SIGN + U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> + <Key + latin:keyLabel="ь" + latin:moreKeys="ъ" /> + <!-- U+0432: "в" CYRILLIC SMALL LETTER VE + U+044E: "ю" CYRILLIC SMALL LETTER YU --> + <Key + latin:keyLabel="в" + latin:moreKeys="ю" /> +</merge> diff --git a/java/res/xml/rowkeys_pcqwerty1.xml b/java/res/xml/rowkeys_pcqwerty1.xml index 0ecda3550..b2d1d374b 100644 --- a/java/res/xml/rowkeys_pcqwerty1.xml +++ b/java/res/xml/rowkeys_pcqwerty1.xml @@ -41,7 +41,7 @@ latin:moreKeys="!text/more_keys_for_symbols_2" /> <Key latin:keyLabel="3" - latin:additionalMoreKeys="#" + latin:additionalMoreKeys="\#" latin:moreKeys="!text/more_keys_for_symbols_3" /> <Key latin:keyLabel="4" diff --git a/java/res/xml/rowkeys_symbols2.xml b/java/res/xml/rowkeys_symbols2.xml index 425e20470..d3c1278da 100644 --- a/java/res/xml/rowkeys_symbols2.xml +++ b/java/res/xml/rowkeys_symbols2.xml @@ -33,14 +33,14 @@ latin:moreKeys="\@" /> <Key latin:keyLabel="٫" - latin:keyHintLabel="#" - latin:moreKeys="#" /> + latin:keyHintLabel="\#" + latin:moreKeys="\#" /> </case> <default> <Key latin:keyLabel="\@" /> <Key - latin:keyLabel="#" /> + latin:keyLabel="\#" /> </default> </switch> <Key diff --git a/java/res/xml/rows_mongolian.xml b/java/res/xml/rows_mongolian.xml new file mode 100644 index 000000000..a6a890a9b --- /dev/null +++ b/java/res/xml/rows_mongolian.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_mongolian1" /> + </Row> + <Row + latin:keyWidth="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_mongolian2" /> + </Row> + <Row + latin:keyWidth="8.711%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.8%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_mongolian3" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml index c59e26247..b77544bc3 100644 --- a/java/res/xml/rows_number_normal.xml +++ b/java/res/xml/rows_number_normal.xml @@ -33,6 +33,8 @@ latin:keyStyle="numKeyStyle" /> <Key latin:keyLabel="-" + latin:moreKeys="+" + latin:keyLabelFlags="hasPopupHint" latin:keyStyle="numFunctionalKeyStyle" latin:keyWidth="fillRight" /> </Row> diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml index 630b24ea4..9299c2aa5 100644 --- a/java/res/xml/rows_phone.xml +++ b/java/res/xml/rows_phone.xml @@ -34,6 +34,8 @@ latin:keyStyle="num3KeyStyle" /> <Key latin:keyLabel="-" + latin:moreKeys="+" + latin:keyLabelFlags="hasPopupHint" latin:keyStyle="numFunctionalKeyStyle" latin:keyWidth="fillRight" /> </Row> diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml index 7841c56e5..8c10a2d71 100644 --- a/java/res/xml/rows_phone_symbols.xml +++ b/java/res/xml/rows_phone_symbols.xml @@ -37,6 +37,8 @@ latin:keyStyle="numKeyStyle" /> <Key latin:keyLabel="-" + latin:moreKeys="+" + latin:keyLabelFlags="hasPopupHint" latin:keyStyle="numFunctionalKeyStyle" latin:keyWidth="fillRight" /> </Row> @@ -63,7 +65,7 @@ <Key latin:keyStyle="numWaitKeyStyle" /> <Key - latin:keyLabel="#" + latin:keyLabel="\#" latin:keyStyle="numKeyStyle" /> <Key latin:keyStyle="deleteKeyStyle" diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 32618ad85..b87ae3a15 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -26,6 +26,7 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -61,17 +62,19 @@ public final class KeyCodeDescriptionMapper { mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); // Special non-character codes defined in Keyboard - mKeyCodeMap.put(Keyboard.CODE_SPACE, R.string.spoken_description_space); - mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete); - mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return); - mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings); - mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift); - mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic); - mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); - mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab); - mKeyCodeMap.put(Keyboard.CODE_LANGUAGE_SWITCH, R.string.spoken_description_language_switch); - mKeyCodeMap.put(Keyboard.CODE_ACTION_NEXT, R.string.spoken_description_action_next); - mKeyCodeMap.put(Keyboard.CODE_ACTION_PREVIOUS, R.string.spoken_description_action_previous); + mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space); + mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete); + mKeyCodeMap.put(Constants.CODE_ENTER, R.string.spoken_description_return); + mKeyCodeMap.put(Constants.CODE_SETTINGS, R.string.spoken_description_settings); + mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift); + mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic); + mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); + mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab); + mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH, + R.string.spoken_description_language_switch); + mKeyCodeMap.put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next); + mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS, + R.string.spoken_description_action_previous); } /** @@ -97,17 +100,17 @@ public final class KeyCodeDescriptionMapper { boolean shouldObscure) { final int code = key.mCode; - if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { + if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard); if (description != null) return description; } - if (code == Keyboard.CODE_SHIFT) { + if (code == Constants.CODE_SHIFT) { return getDescriptionForShiftKey(context, keyboard); } - if (code == Keyboard.CODE_ACTION_ENTER) { + if (code == Constants.CODE_ACTION_ENTER) { return getDescriptionForActionKey(context, keyboard, key); } @@ -121,7 +124,7 @@ public final class KeyCodeDescriptionMapper { } // Just attempt to speak the description. - if (key.mCode != Keyboard.CODE_UNSPECIFIED) { + if (key.mCode != Constants.CODE_UNSPECIFIED) { return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); } diff --git a/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java b/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java new file mode 100644 index 000000000..ea5f12ce2 --- /dev/null +++ b/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.annotations; + +/** + * Denotes that the class, method or field should not be eliminated by ProGuard, + * because it is externally referenced. (See proguard.flags) + */ +public @interface ExternallyReferenced { +} diff --git a/java/src/com/android/inputmethod/annotations/UsedForTesting.java b/java/src/com/android/inputmethod/annotations/UsedForTesting.java new file mode 100644 index 000000000..2ada091e4 --- /dev/null +++ b/java/src/com/android/inputmethod/annotations/UsedForTesting.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.annotations; + +/** + * Denotes that the class, method or field should not be eliminated by ProGuard, + * so that unit tests can access it. (See proguard.flags) + */ +public @interface UsedForTesting { +} diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java index ab7bd4914..8bd1e5208 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java @@ -19,61 +19,21 @@ package com.android.inputmethod.compat; import android.content.Context; import android.os.IBinder; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.ImfUtils; import java.lang.reflect.Method; -// TODO: Override this class with the concrete implementation if we need to take care of the -// performance. public final class InputMethodManagerCompatWrapper { - private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName(); private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod( InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE); - private static final InputMethodManagerCompatWrapper sInstance = - new InputMethodManagerCompatWrapper(); - - private InputMethodManager mImm; - - private InputMethodManagerCompatWrapper() { - // This wrapper class is not publicly instantiable. - } - - public static InputMethodManagerCompatWrapper getInstance() { - if (sInstance.mImm == null) { - throw new RuntimeException(TAG + ".getInstance() is called before initialization"); - } - return sInstance; - } - - public static void init(Context context) { - sInstance.mImm = ImfUtils.getInputMethodManager(context); - } - - public InputMethodSubtype getCurrentInputMethodSubtype() { - return mImm.getCurrentInputMethodSubtype(); - } - - public InputMethodSubtype getLastInputMethodSubtype() { - return mImm.getLastInputMethodSubtype(); - } + public final InputMethodManager mImm; - public boolean switchToLastInputMethod(IBinder token) { - return mImm.switchToLastInputMethod(token); + public InputMethodManagerCompatWrapper(final Context context) { + mImm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); } - public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) { + public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToNextInputMethod, token, onlyCurrentIme); } - - public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) { - mImm.setInputMethodAndSubtype(token, id, subtype); - } - - public void showInputMethodPicker() { - mImm.showInputMethodPicker(); - } } diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 159f43650..c1609ea28 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -83,14 +83,13 @@ public final class SuggestionSpanUtils { } public static CharSequence getTextWithAutoCorrectionIndicatorUnderline( - Context context, CharSequence text) { + final Context context, final String text) { if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null || OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTIONS_MAX_SIZE == null || OBJ_FLAG_MISSPELLED == null || OBJ_FLAG_EASY_CORRECT == null) { return text; } - final Spannable spannable = text instanceof Spannable - ? (Spannable) text : new SpannableString(text); + final Spannable spannable = new SpannableString(text); final Object[] args = { context, null, new String[] {}, (int)OBJ_FLAG_AUTO_CORRECTION, (Class<?>) SuggestionSpanPickedNotificationReceiver.class }; @@ -104,28 +103,24 @@ public final class SuggestionSpanUtils { return spannable; } - public static CharSequence getTextWithSuggestionSpan(Context context, - CharSequence pickedWord, SuggestedWords suggestedWords, boolean dictionaryAvailable) { + public static CharSequence getTextWithSuggestionSpan(final Context context, + final String pickedWord, final SuggestedWords suggestedWords, + final boolean dictionaryAvailable) { if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null - || suggestedWords == null || suggestedWords.size() == 0 - || suggestedWords.mIsPrediction || suggestedWords.mIsPunctuationSuggestions + || suggestedWords.isEmpty() || suggestedWords.mIsPrediction + || suggestedWords.mIsPunctuationSuggestions || OBJ_SUGGESTIONS_MAX_SIZE == null) { return pickedWord; } - final Spannable spannable; - if (pickedWord instanceof Spannable) { - spannable = (Spannable) pickedWord; - } else { - spannable = new SpannableString(pickedWord); - } + final Spannable spannable = new SpannableString(pickedWord); final ArrayList<String> suggestionsList = CollectionUtils.newArrayList(); for (int i = 0; i < suggestedWords.size(); ++i) { if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) { break; } - final CharSequence word = suggestedWords.getWord(i); + final String word = suggestedWords.getWord(i); if (!TextUtils.equals(pickedWord, word)) { suggestionsList.add(word.toString()); } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 30812e8c3..7346a9c38 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -16,11 +16,11 @@ package com.android.inputmethod.keyboard; -import static com.android.inputmethod.keyboard.Keyboard.CODE_OUTPUT_TEXT; -import static com.android.inputmethod.keyboard.Keyboard.CODE_SHIFT; -import static com.android.inputmethod.keyboard.Keyboard.CODE_SWITCH_ALPHA_SYMBOL; -import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED; import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; +import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT; +import static com.android.inputmethod.latin.Constants.CODE_SHIFT; +import static com.android.inputmethod.latin.Constants.CODE_SWITCH_ALPHA_SYMBOL; +import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; import android.content.res.Resources; import android.content.res.TypedArray; @@ -39,6 +39,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.KeyboardRow; import com.android.inputmethod.keyboard.internal.MoreKeySpec; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.ResourceUtils; import com.android.inputmethod.latin.StringUtils; @@ -453,7 +454,7 @@ public class Key implements Comparable<Key> { label = "/" + mLabel; } return String.format("%s%s %d,%d %dx%d %s/%s/%s", - Keyboard.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel, + Constants.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel, KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index aa683c1d7..0a91284d0 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -118,7 +118,7 @@ public class KeyDetector { } public static String printableCode(Key key) { - return key != null ? Keyboard.printableCode(key.mCode) : "none"; + return key != null ? Constants.printableCode(key.mCode) : "none"; } public static String printableCodes(int[] codes) { @@ -127,7 +127,7 @@ public class KeyDetector { for (final int code : codes) { if (code == Constants.NOT_A_CODE) break; if (addDelimiter) sb.append(", "); - sb.append(Keyboard.printableCode(code)); + sb.append(Constants.printableCode(code)); addDelimiter = true; } return "[" + sb + "]"; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index b7c7f415d..a1b1f5dad 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -16,15 +16,13 @@ package com.android.inputmethod.keyboard; -import android.util.Log; import android.util.SparseArray; import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.latin.CollectionUtils; - - +import com.android.inputmethod.latin.Constants; /** * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard @@ -45,46 +43,6 @@ import com.android.inputmethod.latin.CollectionUtils; * </pre> */ public class Keyboard { - private static final String TAG = Keyboard.class.getSimpleName(); - - /** Some common keys code. Must be positive. - * These should be aligned with values/keycodes.xml - */ - public static final int CODE_ENTER = '\n'; - public static final int CODE_TAB = '\t'; - public static final int CODE_SPACE = ' '; - public static final int CODE_PERIOD = '.'; - public static final int CODE_DASH = '-'; - public static final int CODE_SINGLE_QUOTE = '\''; - public static final int CODE_DOUBLE_QUOTE = '"'; - public static final int CODE_QUESTION_MARK = '?'; - public static final int CODE_EXCLAMATION_MARK = '!'; - // TODO: Check how this should work for right-to-left languages. It seems to stand - // that for rtl languages, a closing parenthesis is a left parenthesis. Is this - // managed by the font? Or is it a different char? - public static final int CODE_CLOSING_PARENTHESIS = ')'; - public static final int CODE_CLOSING_SQUARE_BRACKET = ']'; - public static final int CODE_CLOSING_CURLY_BRACKET = '}'; - public static final int CODE_CLOSING_ANGLE_BRACKET = '>'; - - /** Special keys code. Must be negative. - * These should be aligned with KeyboardCodesSet.ID_TO_NAME[], - * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[] - */ - public static final int CODE_SHIFT = -1; - public static final int CODE_SWITCH_ALPHA_SYMBOL = -2; - public static final int CODE_OUTPUT_TEXT = -3; - public static final int CODE_DELETE = -4; - public static final int CODE_SETTINGS = -5; - public static final int CODE_SHORTCUT = -6; - public static final int CODE_ACTION_ENTER = -7; - public static final int CODE_ACTION_NEXT = -8; - public static final int CODE_ACTION_PREVIOUS = -9; - public static final int CODE_LANGUAGE_SWITCH = -10; - public static final int CODE_RESEARCH = -11; - // Code value representing the code is not specified. - public static final int CODE_UNSPECIFIED = -12; - public final KeyboardId mId; public final int mThemeId; @@ -163,7 +121,7 @@ public class Keyboard { } public Key getKey(final int code) { - if (code == CODE_UNSPECIFIED) { + if (code == Constants.CODE_UNSPECIFIED) { return null; } synchronized (mKeyCache) { @@ -197,10 +155,6 @@ public class Keyboard { return false; } - public static boolean isLetterCode(final int code) { - return code >= CODE_SPACE; - } - @Override public String toString() { return mId.toString(); @@ -219,27 +173,4 @@ public class Keyboard { final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1)); return mProximityInfo.getNearestKeys(adjustedX, adjustedY); } - - public static String printableCode(final int code) { - switch (code) { - case CODE_SHIFT: return "shift"; - case CODE_SWITCH_ALPHA_SYMBOL: return "symbol"; - case CODE_OUTPUT_TEXT: return "text"; - case CODE_DELETE: return "delete"; - case CODE_SETTINGS: return "settings"; - case CODE_SHORTCUT: return "shortcut"; - case CODE_ACTION_ENTER: return "actionEnter"; - case CODE_ACTION_NEXT: return "actionNext"; - case CODE_ACTION_PREVIOUS: return "actionPrevious"; - case CODE_LANGUAGE_SWITCH: return "languageSwitch"; - case CODE_UNSPECIFIED: return "unspec"; - case CODE_TAB: return "tab"; - case CODE_ENTER: return "enter"; - default: - if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code); - if (code < CODE_SPACE) return String.format("'\\u%02x'", code); - if (code < 0x100) return String.format("'%c'", code); - return String.format("'\\u%04x'", code); - } - } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 5c8f78f5e..b612f0927 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -56,11 +56,11 @@ public interface KeyboardActionListener { public void onCodeInput(int primaryCode, int x, int y); /** - * Sends a sequence of characters to the listener. + * Sends a string of characters to the listener. * - * @param text the sequence of characters to be displayed. + * @param text the string of characters to be registered. */ - public void onTextInput(CharSequence text); + public void onTextInput(String text); /** * Called when user started batch input. @@ -99,7 +99,7 @@ public interface KeyboardActionListener { @Override public void onCodeInput(int primaryCode, int x, int y) {} @Override - public void onTextInput(CharSequence text) {} + public void onTextInput(String text) {} @Override public void onStartBatchInput() {} @Override @@ -112,13 +112,5 @@ public interface KeyboardActionListener { public boolean onCustomRequest(int requestCode) { return false; } - - // TODO: Remove this method when the vertical correction is removed. - public static boolean isInvalidCoordinate(int coordinate) { - // Detect {@link Constants#NOT_A_COORDINATE}, - // {@link Constants#SUGGESTION_STRIP_COORDINATE}, and - // {@link Constants#SPELL_CHECKER_COORDINATE}. - return coordinate < 0; - } } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index c7813ab02..4d5d7e14e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -34,6 +34,7 @@ import android.util.Xml; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardParams; @@ -194,12 +195,11 @@ public final class KeyboardLayoutSet { final Params params = mParams; final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS || keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED); - final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype); - final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage; - final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain); + final boolean hasShortcutKey = params.mVoiceKeyEnabled + && (isSymbols != params.mVoiceKeyOnMain); return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mDeviceFormFactor, params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo, - params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey, + params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey, params.mLanguageSwitchKeyEnabled); } @@ -266,7 +266,7 @@ public final class KeyboardLayoutSet { return this; } - // For test only + @UsedForTesting public void disableTouchPositionCorrectionDataForTest() { mParams.mDisableTouchPositionCorrectionDataForTest = true; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index de8097b55..c2e31ee1e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -29,12 +29,12 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.keyboard.internal.KeyboardState; -import com.android.inputmethod.latin.DebugSettings; -import com.android.inputmethod.latin.ImfUtils; +import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputView; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.WordComposer; @@ -65,9 +65,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich), }; + private AudioAndHapticFeedbackManager mFeedbackManager; private SubtypeSwitcher mSubtypeSwitcher; private SharedPreferences mPrefs; - private boolean mForceNonDistinctMultitouch; private InputView mCurrentInputView; private MainKeyboardView mKeyboardView; @@ -102,12 +102,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private void initInternal(LatinIME latinIme, SharedPreferences prefs) { mLatinIME = latinIme; mResources = latinIme.getResources(); + mFeedbackManager = new AudioAndHapticFeedbackManager(latinIme); mPrefs = prefs; mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mState = new KeyboardState(this); setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs)); - mForceNonDistinctMultitouch = prefs.getBoolean( - DebugSettings.FORCE_NON_DISTINCT_MULTITOUCH_KEY, false); } private static KeyboardTheme getKeyboardTheme(Context context, SharedPreferences prefs) { @@ -143,10 +142,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { builder.setOptions( settingsValues.isVoiceKeyEnabled(editorInfo), settingsValues.isVoiceKeyOnMain(), - settingsValues.isLanguageSwitchKeyEnabled(mThemeContext)); + settingsValues.isLanguageSwitchKeyEnabled()); mKeyboardLayoutSet = builder.build(); try { mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols)); + mFeedbackManager.onSettingsChanged(settingsValues); } catch (KeyboardLayoutSetException e) { Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause()); @@ -154,6 +154,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } } + public void onRingerModeChanged() { + mFeedbackManager.onRingerModeChanged(); + } + public void saveKeyboardState() { if (getKeyboard() != null) { mState.onSaveKeyboardState(); @@ -183,7 +187,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage( keyboard.mId.mLocale); keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage, - ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true)); + RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true)); } public Keyboard getKeyboard() { @@ -208,7 +212,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { public void onPressKey(int code) { if (isVibrateAndSoundFeedbackRequired()) { - mLatinIME.hapticAndAudioFeedback(code); + mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView); } mState.onPressKey(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState()); } @@ -320,7 +324,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void hapticAndAudioFeedback(int code) { - mLatinIME.hapticAndAudioFeedback(code); + mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView); } public void onLongPressTimeout(int code) { @@ -339,10 +343,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { return mKeyboardView != null && mKeyboardView.getPointerCount() == 1; } - public boolean hasDistinctMultitouch() { - return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch(); - } - /** * Updates state machine to figure out when to automatically switch back to the previous mode. */ @@ -369,9 +369,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? } mKeyboardView.setKeyboardActionListener(mLatinIME); - if (mForceNonDistinctMultitouch) { - mKeyboardView.setDistinctMultitouch(false); - } // This always needs to be set since the accessibility state can // potentially change without the input view being re-created. diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 3e6f92c2a..d5f40ad36 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard; import android.animation.AnimatorInflater; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; @@ -28,7 +29,7 @@ import android.graphics.Paint.Align; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Message; -import android.text.TextUtils; +import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; @@ -41,11 +42,13 @@ import android.widget.PopupWindow; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; +import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.keyboard.internal.KeyDrawParams; -import com.android.inputmethod.keyboard.internal.SuddenJumpingTouchEventHandler; +import com.android.inputmethod.keyboard.internal.TouchScreenRegulator; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.DebugSettings; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -95,7 +98,7 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration */ public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, - SuddenJumpingTouchEventHandler.ProcessMotionEvent { + TouchScreenRegulator.ProcessMotionEvent { private static final String TAG = MainKeyboardView.class.getSimpleName(); // TODO: Kill process when the usability study mode was changed. @@ -138,10 +141,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack new WeakHashMap<Key, MoreKeysPanel>(); private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; - private final SuddenJumpingTouchEventHandler mTouchScreenRegulator; + private final TouchScreenRegulator mTouchScreenRegulator; protected KeyDetector mKeyDetector; - private boolean mHasDistinctMultitouch; + private final boolean mHasDistinctMultitouch; private int mOldPointerCount = 1; private Key mOldKey; @@ -203,7 +206,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) { final Key key = tracker.getKey(); - if (key == null) return; + if (key == null) { + return; + } sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay); } @@ -226,7 +231,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack cancelLongPressTimer(); final int delay; switch (code) { - case Keyboard.CODE_SHIFT: + case Constants.CODE_SHIFT: delay = mLongPressShiftKeyTimeout; break; default: @@ -247,7 +252,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final Key key = tracker.getKey(); final int delay; switch (key.mCode) { - case Keyboard.CODE_SHIFT: + case Constants.CODE_SHIFT: delay = mLongPressShiftKeyTimeout; break; default: @@ -304,7 +309,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack // When user hits the space or the enter key, just cancel the while-typing timer. final int typedCode = typedKey.mCode; - if (typedCode == Keyboard.CODE_SPACE || typedCode == Keyboard.CODE_ENTER) { + if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) { startWhileTypingFadeinAnimation(keyboardView); return; } @@ -356,15 +361,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this); + mTouchScreenRegulator = new TouchScreenRegulator(context, this); - mHasDistinctMultitouch = context.getPackageManager() + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + final boolean forceNonDistinctMultitouch = prefs.getBoolean( + DebugSettings.FORCE_NON_DISTINCT_MULTITOUCH_KEY, false); + final boolean hasDistinctMultitouch = context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); + mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch; final Resources res = getResources(); final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean( ResourceUtils.getDeviceOverrideValue(res, R.array.phantom_sudden_move_event_device_list, "false")); - PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack); + PointerTracker.init(needsPhantomSuddenMoveEventHack); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); @@ -408,7 +417,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } private ObjectAnimator loadObjectAnimator(final int resId, final Object target) { - if (resId == 0) return null; + if (resId == 0) { + return null; + } final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator( getContext(), resId); if (animator != null) { @@ -417,20 +428,23 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return animator; } - // Getter/setter methods for {@link ObjectAnimator}. + @ExternallyReferenced public int getLanguageOnSpacebarAnimAlpha() { return mLanguageOnSpacebarAnimAlpha; } + @ExternallyReferenced public void setLanguageOnSpacebarAnimAlpha(final int alpha) { mLanguageOnSpacebarAnimAlpha = alpha; invalidateKey(mSpaceKey); } + @ExternallyReferenced public int getAltCodeKeyWhileTypingAnimAlpha() { return mAltCodeKeyWhileTypingAnimAlpha; } + @ExternallyReferenced public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) { mAltCodeKeyWhileTypingAnimAlpha = alpha; updateAltCodeKeyWhileTyping(); @@ -480,10 +494,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); PointerTracker.setKeyDetector(mKeyDetector); - mTouchScreenRegulator.setKeyboard(keyboard); + mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth); mMoreKeysPanelCache.clear(); - mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE); + mSpaceKey = keyboard.getKey(Constants.CODE_SPACE); mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null; final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; @@ -506,18 +520,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser); } - /** - * Returns whether the device has distinct multi-touch panel. - * @return true if the device has distinct multi-touch panel. - */ - public boolean hasDistinctMultitouch() { - return mHasDistinctMultitouch; - } - - public void setDistinctMultitouch(final boolean hasDistinctMultitouch) { - mHasDistinctMultitouch = hasDistinctMultitouch; - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -553,21 +555,25 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } // Check if we are already displaying popup panel. - if (mMoreKeysPanel != null) + if (mMoreKeysPanel != null) { return false; - if (parentKey == null) + } + if (parentKey == null) { return false; + } return onLongPress(parentKey, tracker); } // This default implementation returns a more keys panel. protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) { - if (parentKey.mMoreKeys == null) + if (parentKey.mMoreKeys == null) { return null; + } final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null); - if (container == null) + if (container == null) { throw new NullPointerException(); + } final MoreKeysKeyboardView moreKeysKeyboardView = (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view); @@ -600,7 +606,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode); return true; } - if (primaryCode == Keyboard.CODE_SPACE || primaryCode == Keyboard.CODE_LANGUAGE_SWITCH) { + if (primaryCode == Constants.CODE_SPACE || primaryCode == Constants.CODE_LANGUAGE_SWITCH) { // Long pressing the space key invokes IME switcher dialog. if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { tracker.onLongPressed(); @@ -628,8 +634,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey); if (moreKeysPanel == null) { moreKeysPanel = onCreateMoreKeysPanel(parentKey); - if (moreKeysPanel == null) + if (moreKeysPanel == null) { return false; + } mMoreKeysPanelCache.put(parentKey, moreKeysPanel); } if (mMoreKeysWindow == null) { @@ -665,9 +672,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public boolean isInSlidingKeyInput() { if (mMoreKeysPanel != null) { return true; - } else { - return PointerTracker.isAnyInSlidingKeyInput(); } + return PointerTracker.isAnyInSlidingKeyInput(); } public int getPointerCount() { @@ -716,39 +722,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack x = (int)me.getX(index); y = (int)me.getY(index); } - if (ENABLE_USABILITY_STUDY_LOG) { - final String eventTag; - switch (action) { - case MotionEvent.ACTION_UP: - eventTag = "[Up]"; - break; - case MotionEvent.ACTION_DOWN: - eventTag = "[Down]"; - break; - case MotionEvent.ACTION_POINTER_UP: - eventTag = "[PointerUp]"; - break; - case MotionEvent.ACTION_POINTER_DOWN: - eventTag = "[PointerDown]"; - break; - case MotionEvent.ACTION_MOVE: // Skip this as being logged below - eventTag = ""; - break; - default: - eventTag = "[Action" + action + "]"; - break; - } - if (!TextUtils.isEmpty(eventTag)) { - final float size = me.getSize(index); - final float pressure = me.getPressure(index); - UsabilityStudyLogUtils.getInstance().write( - eventTag + eventTime + "," + id + "," + x + "," + y + "," - + size + "," + pressure); - } + // TODO: This might be moved to the tracker.processMotionEvent() call below. + if (ENABLE_USABILITY_STUDY_LOG && action != MotionEvent.ACTION_MOVE) { + writeUsabilityStudyLog(me, action, eventTime, index, id, x, y); } + // TODO: This should be moved to the tracker.processMotionEvent() call below. + // Currently the same "move" event is being logged twice. if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, index, id, - x, y); + ResearchLogger.mainKeyboardView_processMotionEvent( + me, action, eventTime, index, id, x, y); } if (mKeyTimerHandler.isInKeyRepeat()) { @@ -774,8 +756,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final Key newKey = tracker.getKeyOn(x, y); if (mOldKey != newKey) { tracker.onDownEvent(x, y, eventTime, this); - if (action == MotionEvent.ACTION_UP) + if (action == MotionEvent.ACTION_UP) { tracker.onUpEvent(x, y, eventTime); + } } } else if (pointerCount == 2 && oldPointerCount == 1) { // Single-touch to multi-touch transition. @@ -812,15 +795,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } tracker.onMoveEvent(px, py, eventTime, motionEvent); if (ENABLE_USABILITY_STUDY_LOG) { - final float pointerSize = me.getSize(i); - final float pointerPressure = me.getPressure(i); - UsabilityStudyLogUtils.getInstance().write("[Move]" + eventTime + "," - + pointerId + "," + px + "," + py + "," - + pointerSize + "," + pointerPressure); + writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py); } if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, - i, pointerId, px, py); + ResearchLogger.mainKeyboardView_processMotionEvent( + me, action, eventTime, i, pointerId, px, py); } } } else { @@ -831,6 +810,35 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return true; } + private static void writeUsabilityStudyLog(final MotionEvent me, final int action, + final long eventTime, final int index, final int id, final int x, final int y) { + final String eventTag; + switch (action) { + case MotionEvent.ACTION_UP: + eventTag = "[Up]"; + break; + case MotionEvent.ACTION_DOWN: + eventTag = "[Down]"; + break; + case MotionEvent.ACTION_POINTER_UP: + eventTag = "[PointerUp]"; + break; + case MotionEvent.ACTION_POINTER_DOWN: + eventTag = "[PointerDown]"; + break; + case MotionEvent.ACTION_MOVE: + eventTag = "[Move]"; + break; + default: + eventTag = "[Action" + action + "]"; + break; + } + final float size = me.getSize(index); + final float pressure = me.getPressure(index); + UsabilityStudyLogUtils.getInstance().write( + eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + "," + pressure); + } + @Override public void closing() { super.closing(); @@ -840,14 +848,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public boolean dismissMoreKeysPanel() { - if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) { - mMoreKeysWindow.dismiss(); - mMoreKeysPanel = null; - mMoreKeysPanelPointerTrackerId = -1; - dimEntireKeyboard(false); - return true; + if (mMoreKeysWindow == null || !mMoreKeysWindow.isShowing()) { + return false; } - return false; + mMoreKeysWindow.dismiss(); + mMoreKeysPanel = null; + mMoreKeysPanelPointerTrackerId = -1; + dimEntireKeyboard(false); + return true; } /** @@ -870,16 +878,22 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public void updateShortcutKey(final boolean available) { final Keyboard keyboard = getKeyboard(); - if (keyboard == null) return; - final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT); - if (shortcutKey == null) return; + if (keyboard == null) { + return; + } + final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT); + if (shortcutKey == null) { + return; + } shortcutKey.setEnabled(available); invalidateKey(shortcutKey); } private void updateAltCodeKeyWhileTyping() { final Keyboard keyboard = getKeyboard(); - if (keyboard == null) return; + if (keyboard == null) { + return; + } for (final Key key : keyboard.mAltCodeKeysWhileTyping) { invalidateKey(key); } @@ -909,7 +923,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } public void updateAutoCorrectionState(final boolean isAutoCorrection) { - if (!mAutoCorrectionSpacebarLedEnabled) return; + if (!mAutoCorrectionSpacebarLedEnabled) { + return; + } mAutoCorrectionSpacebarLedOn = isAutoCorrection; invalidateKey(mSpaceKey); } @@ -920,13 +936,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (key.altCodeWhileTyping() && key.isEnabled()) { params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; } - if (key.mCode == Keyboard.CODE_SPACE) { + if (key.mCode == Constants.CODE_SPACE) { drawSpacebar(key, canvas, paint); // Whether space key needs to show the "..." popup hint for special purposes if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) { drawKeyPopupHint(key, canvas, paint, params); } - } else if (key.mCode == Keyboard.CODE_LANGUAGE_SWITCH) { + } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) { super.onDrawKeyTopVisuals(key, canvas, paint, params); drawKeyPopupHint(key, canvas, paint, params); } else { @@ -937,10 +953,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) { paint.setTextScaleX(1.0f); final float textWidth = getLabelWidth(text, paint); - if (textWidth < width) return true; + if (textWidth < width) { + return true; + } final float scaleX = width / textWidth; - if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) return false; + if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) { + return false; + } paint.setTextScaleX(scaleX); return getLabelWidth(text, paint) < width; @@ -950,19 +970,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype, final int width) { // Choose appropriate language name to fit into the width. - String text = getFullDisplayName(subtype, getResources()); - if (fitsTextIntoWidth(width, text, paint)) { - return text; + final String fullText = getFullDisplayName(subtype, getResources()); + if (fitsTextIntoWidth(width, fullText, paint)) { + return fullText; } - text = getMiddleDisplayName(subtype); - if (fitsTextIntoWidth(width, text, paint)) { - return text; + final String middleText = getMiddleDisplayName(subtype); + if (fitsTextIntoWidth(width, middleText, paint)) { + return middleText; } - text = getShortDisplayName(subtype); - if (fitsTextIntoWidth(width, text, paint)) { - return text; + final String shortText = getShortDisplayName(subtype); + if (fitsTextIntoWidth(width, shortText, paint)) { + return shortText; } return ""; @@ -1009,18 +1029,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack // InputMethodSubtype's display name for spacebar text in its locale. // isAdditionalSubtype (T=true, F=false) - // locale layout | Short Middle Full - // ------ ------ - ---- --------- ---------------------- - // en_US qwerty F En English English (US) exception - // en_GB qwerty F En English English (UK) exception - // fr azerty F Fr Français Français - // fr_CA qwerty F Fr Français Français (Canada) - // de qwertz F De Deutsch Deutsch - // zz qwerty F QWERTY QWERTY - // fr qwertz T Fr Français Français (QWERTZ) - // de qwerty T De Deutsch Deutsch (QWERTY) - // en_US azerty T En English English (US) (AZERTY) - // zz azerty T AZERTY AZERTY + // locale layout | Short Middle Full + // ------ ------- - ---- --------- ---------------------- + // en_US qwerty F En English English (US) exception + // en_GB qwerty F En English English (UK) exception + // es_US spanish F Es Español Español (EE.UU.) exception + // fr azerty F Fr Français Français + // fr_CA qwerty F Fr Français Français (Canada) + // de qwertz F De Deutsch Deutsch + // zz qwerty F QWERTY QWERTY + // fr qwertz T Fr Français Français (QWERTZ) + // de qwerty T De Deutsch Deutsch (QWERTY) + // en_US azerty T En English English (US) (AZERTY) + // zz azerty T AZERTY AZERTY // Get InputMethodSubtype's full display name in its locale. static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index d7d4be40b..3826a39a4 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -20,6 +20,7 @@ import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.view.View; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; @@ -39,7 +40,7 @@ public final class MoreKeysKeyboard extends Keyboard { return mDefaultKeyCoordX; } - /* package for test */ + @UsedForTesting static class MoreKeysKeyboardParams extends KeyboardParams { public boolean mIsFixedOrder; /* package */int mTopRowAdjustment; diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index a50617693..9c450e994 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -48,7 +48,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys private final KeyboardActionListener mMoreKeysKeyboardListener = new KeyboardActionListener.Adapter() { @Override - public void onCodeInput(int primaryCode, int x, int y) { + public void onCodeInput(final int primaryCode, final int x, final int y) { // Because a more keys keyboard doesn't need proximity characters correction, we don't // send touch event coordinates. mListener.onCodeInput( @@ -56,7 +56,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - public void onTextInput(CharSequence text) { + public void onTextInput(final String text) { mListener.onTextInput(text); } @@ -66,12 +66,12 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - public void onUpdateBatchInput(InputPointers batchPointers) { + public void onUpdateBatchInput(final InputPointers batchPointers) { mListener.onUpdateBatchInput(batchPointers); } @Override - public void onEndBatchInput(InputPointers batchPointers) { + public void onEndBatchInput(final InputPointers batchPointers) { mListener.onEndBatchInput(batchPointers); } @@ -81,21 +81,22 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - public void onPressKey(int primaryCode) { + public void onPressKey(final int primaryCode) { mListener.onPressKey(primaryCode); } @Override - public void onReleaseKey(int primaryCode, boolean withSliding) { + public void onReleaseKey(final int primaryCode, final boolean withSliding) { mListener.onReleaseKey(primaryCode, withSliding); } }; - public MoreKeysKeyboardView(Context context, AttributeSet attrs) { + public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.moreKeysKeyboardViewStyle); } - public MoreKeysKeyboardView(Context context, AttributeSet attrs, int defStyle) { + public MoreKeysKeyboardView(final Context context, final AttributeSet attrs, + final int defStyle) { super(context, attrs, defStyle); final Resources res = context.getResources(); @@ -105,7 +106,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Keyboard keyboard = getKeyboard(); if (keyboard != null) { final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); @@ -117,7 +118,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - public void setKeyboard(Keyboard keyboard) { + public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); @@ -144,15 +145,16 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { + public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) { // More keys keyboard needs no pop-up key preview displayed, so we pass always false with a // delay of 0. The delay does not matter actually since the popup is not shown anyway. super.setKeyPreviewPopupEnabled(false, 0); } @Override - public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY, - PopupWindow window, KeyboardActionListener listener) { + public void showMoreKeysPanel(final View parentView, final Controller controller, + final int pointX, final int pointY, final PopupWindow window, + final KeyboardActionListener listener) { mController = controller; mListener = listener; final View container = (View)getParent(); @@ -185,12 +187,12 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys } @Override - public int translateX(int x) { + public int translateX(final int x) { return x - mOriginX; } @Override - public int translateY(int y) { + public int translateY(final int y) { return y - mOriginY; } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index d0f7cb276..c86805232 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -27,6 +27,7 @@ import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokePara import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -156,7 +157,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true; private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList(); - private static PointerTrackerQueue sPointerTrackerQueue; + private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue(); public final int mPointerId; @@ -325,13 +326,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints; - public static void init(boolean hasDistinctMultitouch, - boolean needsPhantomSuddenMoveEventHack) { - if (hasDistinctMultitouch) { - sPointerTrackerQueue = new PointerTrackerQueue(); - } else { - sPointerTrackerQueue = null; - } + public static void init(final boolean needsPhantomSuddenMoveEventHack) { sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; sParams = PointerTrackerParams.DEFAULT; sGestureStrokeParams = GestureStrokeParams.DEFAULT; @@ -375,7 +370,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } public static boolean isAnyInSlidingKeyInput() { - return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false; + return sPointerTrackerQueue.isAnyInSlidingKeyInput(); } public static void setKeyboardActionListener(final KeyboardActionListener listener) { @@ -453,8 +448,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState(); final int code = altersCode ? key.getAltCode() : primaryCode; if (DEBUG_LISTENER) { - final String output = code == Keyboard.CODE_OUTPUT_TEXT - ? key.getOutputText() : Keyboard.printableCode(code); + final String output = code == Constants.CODE_OUTPUT_TEXT + ? key.getOutputText() : Constants.printableCode(code); Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y, output, ignoreModifierKey ? " ignoreModifier" : "", altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled")); @@ -469,9 +464,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state. if (key.isEnabled() || altersCode) { sTimeRecorder.onCodeInput(code, eventTime); - if (code == Keyboard.CODE_OUTPUT_TEXT) { + if (code == Constants.CODE_OUTPUT_TEXT) { mListener.onTextInput(key.getOutputText()); - } else if (code != Keyboard.CODE_UNSPECIFIED) { + } else if (code != Constants.CODE_UNSPECIFIED) { mListener.onCodeInput(code, x, y); } } @@ -487,7 +482,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier(); if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId, - Keyboard.printableCode(primaryCode), + Constants.printableCode(primaryCode), withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "", key.isEnabled() ? "": " disabled")); } @@ -682,7 +677,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } private static int getActivePointerTrackerCount() { - return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size(); + return sPointerTrackerQueue.size(); + } + + private static boolean isOldestTrackerInQueue(final PointerTracker tracker) { + return sPointerTrackerQueue.getOldestElement() == tracker; } private void mayStartBatchInput(final Key key) { @@ -702,8 +701,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { sLastRecognitionTime = 0; mListener.onStartBatchInput(); } - final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; - mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); + mTimerProxy.cancelLongPressTimer(); + mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); } private void mayUpdateBatchInput(final long eventTime, final Key key) { @@ -724,8 +723,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } } - final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; - mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); + mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); } private void mayEndBatchInput(final long eventTime) { @@ -741,8 +739,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mListener.onEndBatchInput(sAggregratedPointers); } } - final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; - mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); + mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this)); } public void processMotionEvent(final int action, final int x, final int y, final long eventTime, @@ -794,22 +791,19 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final Key key = getKeyOn(x, y); mBogusMoveEventDetector.onActualDownEvent(x, y); - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - if (key != null && key.isModifier()) { - // Before processing a down event of modifier key, all pointers already being - // tracked should be released. - queue.releaseAllPointers(eventTime); - } - queue.add(this); + if (key != null && key.isModifier()) { + // Before processing a down event of modifier key, all pointers already being + // tracked should be released. + sPointerTrackerQueue.releaseAllPointers(eventTime); } + sPointerTrackerQueue.add(this); onDownEventInternal(x, y, eventTime); if (!sShouldHandleGesture) { return; } - // A gesture should start only from the letter key. + // A gesture should start only from a non-modifier key. mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard() - && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode); + && !mIsShowingMoreKeysPanel && key != null && !key.isModifier(); if (mIsDetectingGesture) { if (getActivePointerTrackerCount() == 1) { sGestureFirstDownTime = eventTime; @@ -891,136 +885,150 @@ public final class PointerTracker implements PointerTrackerQueue.Element { onMoveEventInternal(x, y, eventTime); } + private void processSlidingKeyInput(final Key newKey, final int x, final int y, + final long eventTime) { + // This onPress call may have changed keyboard layout. Those cases are detected + // at {@link #setKeyboard}. In those cases, we should update key according + // to the new keyboard layout. + Key key = newKey; + if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { + key = onMoveKey(x, y); + } + onMoveToNewKey(key, x, y); + startLongPressTimer(key); + setPressedKeyGraphics(key, eventTime); + } + + private void processPhantomSuddenMoveHack(final Key key, final int x, final int y, + final long eventTime, final Key oldKey, final int lastX, final int lastY) { + if (DEBUG_MODE) { + Log.w(TAG, String.format("[%d] onMoveEvent:" + + " phantom sudden move event (distance=%d) is translated to " + + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId, + getDistance(x, y, lastX, lastY), + lastX, lastY, Constants.printableCode(oldKey.mCode), + x, y, Constants.printableCode(key.mCode))); + } + // TODO: This should be moved to outside of this nested if-clause? + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); + } + onUpEventInternal(eventTime); + onDownEventInternal(x, y, eventTime); + } + + private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y, + final long eventTime, final Key oldKey, final int lastX, final int lastY) { + if (DEBUG_MODE) { + final float keyDiagonal = (float)Math.hypot( + mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); + final float radiusRatio = + mBogusMoveEventDetector.getDistanceFromDownEvent(x, y) + / keyDiagonal; + Log.w(TAG, String.format("[%d] onMoveEvent:" + + " bogus down-move-up event (raidus=%.2f key diagonal) is " + + " translated to up[%d,%d,%s]/down[%d,%d,%s] events", + mPointerId, radiusRatio, + lastX, lastY, Constants.printableCode(oldKey.mCode), + x, y, Constants.printableCode(key.mCode))); + } + onUpEventInternal(eventTime); + onDownEventInternal(x, y, eventTime); + } + + private void processSildeOutFromOldKey(final Key oldKey) { + setReleasedKeyGraphics(oldKey); + callListenerOnRelease(oldKey, oldKey.mCode, true); + startSlidingKeyInput(oldKey); + mTimerProxy.cancelKeyTimers(); + } + + private void slideFromOldKeyToNewKey(final Key key, final int x, final int y, + final long eventTime, final Key oldKey, final int lastX, final int lastY) { + // The pointer has been slid in to the new key from the previous key, we must call + // onRelease() first to notify that the previous key has been released, then call + // onPress() to notify that the new key is being pressed. + processSildeOutFromOldKey(oldKey); + startRepeatKey(key); + if (mIsAllowedSlidingKeyInput) { + processSlidingKeyInput(key, x, y, eventTime); + } + // HACK: On some devices, quick successive touches may be reported as a sudden move by + // touch panel firmware. This hack detects such cases and translates the move event to + // successive up and down events. + // TODO: Should find a way to balance gesture detection and this hack. + else if (sNeedsPhantomSuddenMoveEventHack + && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) { + processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY); + } + // HACK: On some devices, quick successive proximate touches may be reported as a bogus + // down-move-up event by touch panel firmware. This hack detects such cases and breaks + // these events into separate up and down events. + else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime) + && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) { + processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY); + } + // HACK: If there are currently multiple touches, register the key even if the finger + // slides off the key. This defends against noise from some touch panels when there are + // close multiple touches. + // Caveat: When in chording input mode with a modifier key, we don't use this hack. + else if (getActivePointerTrackerCount() > 1 + && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { + if (DEBUG_MODE) { + Log.w(TAG, String.format("[%d] onMoveEvent:" + + " detected sliding finger while multi touching", mPointerId)); + } + onUpEvent(x, y, eventTime); + mKeyAlreadyProcessed = true; + setReleasedKeyGraphics(oldKey); + } else { + if (!mIsDetectingGesture) { + mKeyAlreadyProcessed = true; + } + setReleasedKeyGraphics(oldKey); + } + } + + private void slideOutFromOldKey(final Key oldKey, final int x, final int y) { + // The pointer has been slid out from the previous key, we must call onRelease() to + // notify that the previous key has been released. + processSildeOutFromOldKey(oldKey); + if (mIsAllowedSlidingKeyInput) { + onMoveToNewKey(null, x, y); + } else { + if (!mIsDetectingGesture) { + mKeyAlreadyProcessed = true; + } + } + } + private void onMoveEventInternal(final int x, final int y, final long eventTime) { final int lastX = mLastX; final int lastY = mLastY; final Key oldKey = mCurrentKey; - Key key = onMoveKey(x, y); + final Key newKey = onMoveKey(x, y); if (sShouldHandleGesture) { // Register move event on gesture tracker. - onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key); + onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey); if (sInGesture) { - mTimerProxy.cancelLongPressTimer(); mCurrentKey = null; setReleasedKeyGraphics(oldKey); return; } } - if (key != null) { - if (oldKey == null) { + if (newKey != null) { + if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) { + slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY); + } else if (oldKey == null) { // The pointer has been slid in to the new key, but the finger was not on any keys. // In this case, we must call onPress() to notify that the new key is being pressed. - // This onPress call may have changed keyboard layout. Those cases are detected at - // {@link #setKeyboard}. In those cases, we should update key according to the - // new keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { - key = onMoveKey(x, y); - } - onMoveToNewKey(key, x, y); - startLongPressTimer(key); - setPressedKeyGraphics(key, eventTime); - } else if (isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) { - // The pointer has been slid in to the new key from the previous key, we must call - // onRelease() first to notify that the previous key has been released, then call - // onPress() to notify that the new key is being pressed. - setReleasedKeyGraphics(oldKey); - callListenerOnRelease(oldKey, oldKey.mCode, true); - startSlidingKeyInput(oldKey); - mTimerProxy.cancelKeyTimers(); - startRepeatKey(key); - if (mIsAllowedSlidingKeyInput) { - // This onPress call may have changed keyboard layout. Those cases are detected - // at {@link #setKeyboard}. In those cases, we should update key according - // to the new keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { - key = onMoveKey(x, y); - } - onMoveToNewKey(key, x, y); - startLongPressTimer(key); - setPressedKeyGraphics(key, eventTime); - } else { - // HACK: On some devices, quick successive touches may be reported as a sudden - // move by touch panel firmware. This hack detects such cases and translates the - // move event to successive up and down events. - // TODO: Should find a way to balance gesture detection and this hack. - if (sNeedsPhantomSuddenMoveEventHack - && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) { - if (DEBUG_MODE) { - Log.w(TAG, String.format("[%d] onMoveEvent:" - + " phantom sudden move event (distance=%d) is translated to " - + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId, - getDistance(x, y, lastX, lastY), - lastX, lastY, Keyboard.printableCode(oldKey.mCode), - x, y, Keyboard.printableCode(key.mCode))); - } - // TODO: This should be moved to outside of this nested if-clause? - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); - } - onUpEventInternal(eventTime); - onDownEventInternal(x, y, eventTime); - } - // HACK: On some devices, quick successive proximate touches may be reported as - // a bogus down-move-up event by touch panel firmware. This hack detects such - // cases and breaks these events into separate up and down events. - else if (sNeedsProximateBogusDownMoveUpEventHack - && sTimeRecorder.isInFastTyping(eventTime) - && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) { - if (DEBUG_MODE) { - final float keyDiagonal = (float)Math.hypot( - mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); - final float radiusRatio = - mBogusMoveEventDetector.getDistanceFromDownEvent(x, y) - / keyDiagonal; - Log.w(TAG, String.format("[%d] onMoveEvent:" - + " bogus down-move-up event (raidus=%.2f key diagonal) is " - + " translated to up[%d,%d,%s]/down[%d,%d,%s] events", - mPointerId, radiusRatio, - lastX, lastY, Keyboard.printableCode(oldKey.mCode), - x, y, Keyboard.printableCode(key.mCode))); - } - onUpEventInternal(eventTime); - onDownEventInternal(x, y, eventTime); - } else { - // HACK: If there are currently multiple touches, register the key even if - // the finger slides off the key. This defends against noise from some - // touch panels when there are close multiple touches. - // Caveat: When in chording input mode with a modifier key, we don't use - // this hack. - if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null - && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { - if (DEBUG_MODE) { - Log.w(TAG, String.format("[%d] onMoveEvent:" - + " detected sliding finger while multi touching", - mPointerId)); - } - onUpEvent(x, y, eventTime); - mKeyAlreadyProcessed = true; - } - if (!mIsDetectingGesture) { - mKeyAlreadyProcessed = true; - } - setReleasedKeyGraphics(oldKey); - } - } + processSlidingKeyInput(newKey, x, y, eventTime); } - } else { - if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) { - // The pointer has been slid out from the previous key, we must call onRelease() to - // notify that the previous key has been released. - setReleasedKeyGraphics(oldKey); - callListenerOnRelease(oldKey, oldKey.mCode, true); - startSlidingKeyInput(oldKey); - mTimerProxy.cancelLongPressTimer(); - if (mIsAllowedSlidingKeyInput) { - onMoveToNewKey(key, x, y); - } else { - if (!mIsDetectingGesture) { - mKeyAlreadyProcessed = true; - } - } + } else { // newKey == null + if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) { + slideOutFromOldKey(oldKey, x, y); } } } @@ -1030,22 +1038,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element { printTouchEvent("onUpEvent :", x, y, eventTime); } - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - if (!sInGesture) { - if (mCurrentKey != null && mCurrentKey.isModifier()) { - // Before processing an up event of modifier key, all pointers already being - // tracked should be released. - queue.releaseAllPointersExcept(this, eventTime); - } else { - queue.releaseAllPointersOlderThan(this, eventTime); - } + if (!sInGesture) { + if (mCurrentKey != null && mCurrentKey.isModifier()) { + // Before processing an up event of modifier key, all pointers already being + // tracked should be released. + sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime); + } else { + sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime); } } onUpEventInternal(eventTime); - if (queue != null) { - queue.remove(this); - } + sPointerTrackerQueue.remove(this); } // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event. @@ -1098,10 +1101,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public void onLongPressed() { mKeyAlreadyProcessed = true; setReleasedKeyGraphics(mCurrentKey); - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - queue.remove(this); - } + sPointerTrackerQueue.remove(this); } public void onCancelEvent(final int x, final int y, final long eventTime) { @@ -1109,11 +1109,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { printTouchEvent("onCancelEvt:", x, y, eventTime); } - final PointerTrackerQueue queue = sPointerTrackerQueue; - if (queue != null) { - queue.releaseAllPointersExcept(this, eventTime); - queue.remove(this); - } + sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime); + sPointerTrackerQueue.remove(this); onCancelEventInternal(); } @@ -1149,38 +1146,38 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final Key curKey = mCurrentKey; if (newKey == curKey) { return false; - } else if (curKey != null) { - final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared( - mIsInSlidingKeyInputFromModifier); - final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y); - if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) { - if (DEBUG_MODE) { - final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared) - / mKeyboard.mMostCommonKeyWidth; - Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" - +" %.2f key width from key edge", - mPointerId, distanceToEdgeRatio)); - } - return true; + } + if (curKey == null /* && newKey != null */) { + return true; + } + // Here curKey points to the different key from newKey. + final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared( + mIsInSlidingKeyInputFromModifier); + final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y); + if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) { + if (DEBUG_MODE) { + final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared) + / mKeyboard.mMostCommonKeyWidth; + Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" + +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio)); } - if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput - && sTimeRecorder.isInFastTyping(eventTime) - && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) { - if (DEBUG_MODE) { - final float keyDiagonal = (float)Math.hypot( - mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); - final float lengthFromDownRatio = - mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal; - Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" - + " %.2f key diagonal from virtual down point", - mPointerId, lengthFromDownRatio)); - } - return true; + return true; + } + if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput + && sTimeRecorder.isInFastTyping(eventTime) + && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) { + if (DEBUG_MODE) { + final float keyDiagonal = (float)Math.hypot( + mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); + final float lengthFromDownRatio = + mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal; + Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:" + + " %.2f key diagonal from virtual down point", + mPointerId, lengthFromDownRatio)); } - return false; - } else { // curKey == null && newKey != null return true; } + return false; } private void startLongPressTimer(final Key key) { diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 06a9e9252..ed01f3458 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard; import android.graphics.Rect; import android.text.TextUtils; +import android.util.Log; import com.android.inputmethod.keyboard.internal.TouchPositionCorrection; import com.android.inputmethod.latin.Constants; @@ -26,11 +27,14 @@ import com.android.inputmethod.latin.JniUtils; import java.util.Arrays; public final class ProximityInfo { + private static final String TAG = ProximityInfo.class.getSimpleName(); + private static final boolean DEBUG = false; + /** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL * in defines.h */ public static final int MAX_PROXIMITY_CHARS_SIZE = 16; /** Number of key widths from current touch point to search for nearest keys. */ - private static float SEARCH_DISTANCE = 1.2f; + private static final float SEARCH_DISTANCE = 1.2f; private static final Key[] EMPTY_KEY_ARRAY = new Key[0]; private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f; @@ -140,9 +144,13 @@ public final class ProximityInfo { } if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { + if (DEBUG) { + Log.d(TAG, "touchPositionCorrection: ON"); + } sweetSpotCenterXs = new float[keyCount]; sweetSpotCenterYs = new float[keyCount]; sweetSpotRadii = new float[keyCount]; + final int rows = touchPositionCorrection.getRows(); final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS * (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight); for (int i = 0; i < keyCount; i++) { @@ -152,7 +160,7 @@ public final class ProximityInfo { sweetSpotCenterYs[i] = hitBox.exactCenterY(); sweetSpotRadii[i] = defaultRadius; final int row = hitBox.top / mMostCommonKeyHeight; - if (row < touchPositionCorrection.getRows()) { + if (row < rows) { final int hitBoxWidth = hitBox.width(); final int hitBoxHeight = hitBox.height(); final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight); @@ -160,9 +168,19 @@ public final class ProximityInfo { sweetSpotCenterYs[i] += touchPositionCorrection.getY(row) * hitBoxHeight; sweetSpotRadii[i] = touchPositionCorrection.getRadius(row) * hitBoxDiagonal; } + if (DEBUG) { + Log.d(TAG, String.format( + " [%2d] row=%d x/y/r=%7.2f/%7.2f/%5.2f %s code=%s", i, row, + sweetSpotCenterXs[i], sweetSpotCenterYs[i], sweetSpotRadii[i], + (row < rows ? "correct" : "default"), + Constants.printableCode(key.mCode))); + } } } else { sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null; + if (DEBUG) { + Log.d(TAG, "touchPositionCorrection: OFF"); + } } return setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE, @@ -221,7 +239,7 @@ public final class ProximityInfo { return; } int index = 0; - if (primaryKeyCode > Keyboard.CODE_SPACE) { + if (primaryKeyCode > Constants.CODE_SPACE) { dest[index++] = primaryKeyCode; } final Key[] nearestKeys = getNearestKeys(x, y); @@ -230,7 +248,7 @@ public final class ProximityInfo { break; } final int code = key.mCode; - if (code <= Keyboard.CODE_SPACE) { + if (code <= Constants.CODE_SPACE) { break; } dest[index++] = code; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 2caa5eb02..9f6e2f37e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -16,12 +16,13 @@ package com.android.inputmethod.keyboard.internal; -import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED; +import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT; +import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; import android.text.TextUtils; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.StringUtils; @@ -172,7 +173,7 @@ public final class KeySpecParser { if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); } - return parseCode(moreKeySpec.substring(end + 1), codesSet, Keyboard.CODE_UNSPECIFIED); + return parseCode(moreKeySpec.substring(end + 1), codesSet, CODE_UNSPECIFIED); } final String outputText = getOutputTextInternal(moreKeySpec); if (outputText != null) { @@ -181,14 +182,14 @@ public final class KeySpecParser { if (StringUtils.codePointCount(outputText) == 1) { return outputText.codePointAt(0); } - return Keyboard.CODE_OUTPUT_TEXT; + return CODE_OUTPUT_TEXT; } final String label = getLabel(moreKeySpec); // Code is automatically generated for one letter label. if (StringUtils.codePointCount(label) == 1) { return label.codePointAt(0); } - return Keyboard.CODE_OUTPUT_TEXT; + return CODE_OUTPUT_TEXT; } public static int parseCode(final String text, final KeyboardCodesSet codesSet, @@ -468,7 +469,7 @@ public final class KeySpecParser { public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, final Locale locale) { - if (!Keyboard.isLetterCode(code) || !needsToUpperCase) return code; + if (!Constants.isLetterCode(code) || !needsToUpperCase) return code; final String text = new String(new int[] { code } , 0, 1); final String casedText = KeySpecParser.toUpperCaseOfStringForLocale( text, needsToUpperCase, locale); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index b314a3795..36342688e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -27,6 +27,7 @@ import android.util.TypedValue; import android.util.Xml; import android.view.InflateException; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; @@ -177,7 +178,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { return this; } - // For test only + @UsedForTesting public void disableTouchPositionCorrectionDataForTest() { mParams.mTouchPositionCorrection.setEnabled(false); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index 840d7133d..428e31ccd 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -16,8 +16,8 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; import java.util.HashMap; @@ -74,21 +74,21 @@ public final class KeyboardCodesSet { private static final int CODE_RIGHT_CURLY_BRACKET = '}'; private static final int[] DEFAULT = { - Keyboard.CODE_TAB, - Keyboard.CODE_ENTER, - Keyboard.CODE_SPACE, - Keyboard.CODE_SHIFT, - Keyboard.CODE_SWITCH_ALPHA_SYMBOL, - Keyboard.CODE_OUTPUT_TEXT, - Keyboard.CODE_DELETE, - Keyboard.CODE_SETTINGS, - Keyboard.CODE_SHORTCUT, - Keyboard.CODE_ACTION_ENTER, - Keyboard.CODE_ACTION_NEXT, - Keyboard.CODE_ACTION_PREVIOUS, - Keyboard.CODE_LANGUAGE_SWITCH, - Keyboard.CODE_RESEARCH, - Keyboard.CODE_UNSPECIFIED, + Constants.CODE_TAB, + Constants.CODE_ENTER, + Constants.CODE_SPACE, + Constants.CODE_SHIFT, + Constants.CODE_SWITCH_ALPHA_SYMBOL, + Constants.CODE_OUTPUT_TEXT, + Constants.CODE_DELETE, + Constants.CODE_SETTINGS, + Constants.CODE_SHORTCUT, + Constants.CODE_ACTION_ENTER, + Constants.CODE_ACTION_NEXT, + Constants.CODE_ACTION_PREVIOUS, + Constants.CODE_LANGUAGE_SWITCH, + Constants.CODE_RESEARCH, + Constants.CODE_UNSPECIFIED, CODE_LEFT_PARENTHESIS, CODE_RIGHT_PARENTHESIS, CODE_LESS_THAN_SIGN, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java index e6fe50e02..642e1a18c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java @@ -19,9 +19,9 @@ package com.android.inputmethod.keyboard.internal; import android.util.SparseIntArray; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; import java.util.ArrayList; import java.util.TreeSet; @@ -89,7 +89,7 @@ public class KeyboardParams { mKeys.add(key); updateHistogram(key); } - if (key.mCode == Keyboard.CODE_SHIFT) { + if (key.mCode == Constants.CODE_SHIFT) { mShiftKeys.add(key); } if (key.altCodeWhileTyping()) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 5e111fb9a..804a34b81 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -19,7 +19,6 @@ package com.android.inputmethod.keyboard.internal; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.Constants; /** @@ -316,12 +315,12 @@ public final class KeyboardState { public void onPressKey(int code, boolean isSinglePointer, int autoCaps) { if (DEBUG_EVENT) { - Log.d(TAG, "onPressKey: code=" + Keyboard.printableCode(code) + Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) + " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this); } - if (code == Keyboard.CODE_SHIFT) { + if (code == Constants.CODE_SHIFT) { onPressShift(); - } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { + } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { onPressSymbol(); } else { mSwitchActions.cancelDoubleTapTimer(); @@ -349,12 +348,12 @@ public final class KeyboardState { public void onReleaseKey(int code, boolean withSliding) { if (DEBUG_EVENT) { - Log.d(TAG, "onReleaseKey: code=" + Keyboard.printableCode(code) + Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code) + " sliding=" + withSliding + " " + this); } - if (code == Keyboard.CODE_SHIFT) { + if (code == Constants.CODE_SHIFT) { onReleaseShift(withSliding); - } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { + } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { onReleaseSymbol(withSliding); } } @@ -381,9 +380,9 @@ public final class KeyboardState { public void onLongPressTimeout(int code) { if (DEBUG_EVENT) { - Log.d(TAG, "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " " + this); + Log.d(TAG, "onLongPressTimeout: code=" + Constants.printableCode(code) + " " + this); } - if (mIsAlphabetMode && code == Keyboard.CODE_SHIFT) { + if (mIsAlphabetMode && code == Constants.CODE_SHIFT) { mLongPressShiftLockFired = true; mSwitchActions.hapticAndAudioFeedback(code); } @@ -459,7 +458,7 @@ public final class KeyboardState { setShifted(MANUAL_SHIFT); mShiftKeyState.onPress(); } - mSwitchActions.startLongPressTimer(Keyboard.CODE_SHIFT); + mSwitchActions.startLongPressTimer(Constants.CODE_SHIFT); } } else { // In symbol mode, just toggle symbol and symbol more keyboard. @@ -539,7 +538,7 @@ public final class KeyboardState { } private static boolean isSpaceCharacter(int c) { - return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER; + return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER; } private boolean isLayoutSwitchBackCharacter(int c) { @@ -550,14 +549,14 @@ public final class KeyboardState { public void onCodeInput(int code, boolean isSinglePointer, int autoCaps) { if (DEBUG_EVENT) { - Log.d(TAG, "onCodeInput: code=" + Keyboard.printableCode(code) + Log.d(TAG, "onCodeInput: code=" + Constants.printableCode(code) + " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this); } switch (mSwitchState) { case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: - if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { + if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { // Detected only the mode change key has been pressed, and then released. if (mIsAlphabetMode) { mSwitchState = SWITCH_STATE_ALPHA; @@ -573,7 +572,7 @@ public final class KeyboardState { } break; case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: - if (code == Keyboard.CODE_SHIFT) { + if (code == Constants.CODE_SHIFT) { // Detected only the shift key has been pressed on symbol layout, and then released. mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } else if (isSinglePointer) { @@ -584,8 +583,8 @@ public final class KeyboardState { } break; case SWITCH_STATE_SYMBOL_BEGIN: - if (!isSpaceCharacter(code) && (Keyboard.isLetterCode(code) - || code == Keyboard.CODE_OUTPUT_TEXT)) { + if (!isSpaceCharacter(code) && (Constants.isLetterCode(code) + || code == Constants.CODE_OUTPUT_TEXT)) { mSwitchState = SWITCH_STATE_SYMBOL; } // Switch back to alpha keyboard mode immediately if user types one of the switch back @@ -606,7 +605,7 @@ public final class KeyboardState { } // If the code is a letter, update keyboard shift state. - if (Keyboard.isLetterCode(code)) { + if (Constants.isLetterCode(code)) { updateAlphabetShiftState(autoCaps); } } @@ -634,7 +633,7 @@ public final class KeyboardState { @Override public String toString() { return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString() - : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS")) + : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS")) + " shift=" + mShiftKeyState + " symbol=" + mSymbolKeyState + " switch=" + switchStateToString(mSwitchState) + "]"; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 3bb272f8c..9d0564315 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; @@ -64,7 +65,7 @@ public final class KeyboardTextsSet { loadStringResourcesInternal(context, RESOURCE_NAMES, R.string.english_ime_name); } - /* package for test */ + @UsedForTesting void loadStringResourcesInternal(Context context, final String[] resourceNames, int referenceId) { final Resources res = context.getResources(); @@ -150,89 +151,88 @@ public final class KeyboardTextsSet { /* 43 */ "more_keys_for_double_quote", /* 44 */ "more_keys_for_tablet_double_quote", /* 45 */ "more_keys_for_currency_dollar", - /* 46 */ "more_keys_for_currency_euro", - /* 47 */ "more_keys_for_currency_pound", - /* 48 */ "more_keys_for_currency_general", - /* 49 */ "more_keys_for_punctuation", - /* 50 */ "more_keys_for_star", - /* 51 */ "more_keys_for_bullet", - /* 52 */ "more_keys_for_plus", - /* 53 */ "more_keys_for_left_parenthesis", - /* 54 */ "more_keys_for_right_parenthesis", - /* 55 */ "more_keys_for_less_than", - /* 56 */ "more_keys_for_greater_than", - /* 57 */ "more_keys_for_arabic_diacritics", - /* 58 */ "keyhintlabel_for_arabic_diacritics", - /* 59 */ "keylabel_for_symbols_1", - /* 60 */ "keylabel_for_symbols_2", - /* 61 */ "keylabel_for_symbols_3", - /* 62 */ "keylabel_for_symbols_4", - /* 63 */ "keylabel_for_symbols_5", - /* 64 */ "keylabel_for_symbols_6", - /* 65 */ "keylabel_for_symbols_7", - /* 66 */ "keylabel_for_symbols_8", - /* 67 */ "keylabel_for_symbols_9", - /* 68 */ "keylabel_for_symbols_0", - /* 69 */ "additional_more_keys_for_symbols_1", - /* 70 */ "additional_more_keys_for_symbols_2", - /* 71 */ "additional_more_keys_for_symbols_3", - /* 72 */ "additional_more_keys_for_symbols_4", - /* 73 */ "additional_more_keys_for_symbols_5", - /* 74 */ "additional_more_keys_for_symbols_6", - /* 75 */ "additional_more_keys_for_symbols_7", - /* 76 */ "additional_more_keys_for_symbols_8", - /* 77 */ "additional_more_keys_for_symbols_9", - /* 78 */ "additional_more_keys_for_symbols_0", - /* 79 */ "more_keys_for_symbols_1", - /* 80 */ "more_keys_for_symbols_2", - /* 81 */ "more_keys_for_symbols_3", - /* 82 */ "more_keys_for_symbols_4", - /* 83 */ "more_keys_for_symbols_5", - /* 84 */ "more_keys_for_symbols_6", - /* 85 */ "more_keys_for_symbols_7", - /* 86 */ "more_keys_for_symbols_8", - /* 87 */ "more_keys_for_symbols_9", - /* 88 */ "more_keys_for_symbols_0", - /* 89 */ "keylabel_for_comma", - /* 90 */ "more_keys_for_comma", - /* 91 */ "keylabel_for_symbols_question", - /* 92 */ "keylabel_for_symbols_semicolon", - /* 93 */ "keylabel_for_symbols_percent", - /* 94 */ "more_keys_for_symbols_exclamation", - /* 95 */ "more_keys_for_symbols_question", - /* 96 */ "more_keys_for_symbols_semicolon", - /* 97 */ "more_keys_for_symbols_percent", - /* 98 */ "keylabel_for_tablet_comma", - /* 99 */ "keyhintlabel_for_tablet_comma", - /* 100 */ "more_keys_for_tablet_comma", - /* 101 */ "keyhintlabel_for_tablet_period", - /* 102 */ "more_keys_for_tablet_period", - /* 103 */ "keylabel_for_apostrophe", - /* 104 */ "keyhintlabel_for_apostrophe", - /* 105 */ "more_keys_for_apostrophe", - /* 106 */ "more_keys_for_q", - /* 107 */ "more_keys_for_x", - /* 108 */ "keylabel_for_q", - /* 109 */ "keylabel_for_w", - /* 110 */ "keylabel_for_y", - /* 111 */ "keylabel_for_x", - /* 112 */ "keylabel_for_spanish_row2_10", - /* 113 */ "more_keys_for_am_pm", - /* 114 */ "settings_as_more_key", - /* 115 */ "shortcut_as_more_key", - /* 116 */ "action_next_as_more_key", - /* 117 */ "action_previous_as_more_key", - /* 118 */ "label_to_more_symbol_key", - /* 119 */ "label_to_more_symbol_for_tablet_key", - /* 120 */ "label_tab_key", - /* 121 */ "label_to_phone_numeric_key", - /* 122 */ "label_to_phone_symbols_key", - /* 123 */ "label_time_am", - /* 124 */ "label_time_pm", - /* 125 */ "label_to_symbol_key_pcqwerty", - /* 126 */ "keylabel_for_popular_domain", - /* 127 */ "more_keys_for_popular_domain", - /* 128 */ "more_keys_for_smiley", + /* 46 */ "keylabel_for_currency_generic", + /* 47 */ "more_keys_for_currency_generic", + /* 48 */ "more_keys_for_punctuation", + /* 49 */ "more_keys_for_star", + /* 50 */ "more_keys_for_bullet", + /* 51 */ "more_keys_for_plus", + /* 52 */ "more_keys_for_left_parenthesis", + /* 53 */ "more_keys_for_right_parenthesis", + /* 54 */ "more_keys_for_less_than", + /* 55 */ "more_keys_for_greater_than", + /* 56 */ "more_keys_for_arabic_diacritics", + /* 57 */ "keyhintlabel_for_arabic_diacritics", + /* 58 */ "keylabel_for_symbols_1", + /* 59 */ "keylabel_for_symbols_2", + /* 60 */ "keylabel_for_symbols_3", + /* 61 */ "keylabel_for_symbols_4", + /* 62 */ "keylabel_for_symbols_5", + /* 63 */ "keylabel_for_symbols_6", + /* 64 */ "keylabel_for_symbols_7", + /* 65 */ "keylabel_for_symbols_8", + /* 66 */ "keylabel_for_symbols_9", + /* 67 */ "keylabel_for_symbols_0", + /* 68 */ "additional_more_keys_for_symbols_1", + /* 69 */ "additional_more_keys_for_symbols_2", + /* 70 */ "additional_more_keys_for_symbols_3", + /* 71 */ "additional_more_keys_for_symbols_4", + /* 72 */ "additional_more_keys_for_symbols_5", + /* 73 */ "additional_more_keys_for_symbols_6", + /* 74 */ "additional_more_keys_for_symbols_7", + /* 75 */ "additional_more_keys_for_symbols_8", + /* 76 */ "additional_more_keys_for_symbols_9", + /* 77 */ "additional_more_keys_for_symbols_0", + /* 78 */ "more_keys_for_symbols_1", + /* 79 */ "more_keys_for_symbols_2", + /* 80 */ "more_keys_for_symbols_3", + /* 81 */ "more_keys_for_symbols_4", + /* 82 */ "more_keys_for_symbols_5", + /* 83 */ "more_keys_for_symbols_6", + /* 84 */ "more_keys_for_symbols_7", + /* 85 */ "more_keys_for_symbols_8", + /* 86 */ "more_keys_for_symbols_9", + /* 87 */ "more_keys_for_symbols_0", + /* 88 */ "keylabel_for_comma", + /* 89 */ "more_keys_for_comma", + /* 90 */ "keylabel_for_symbols_question", + /* 91 */ "keylabel_for_symbols_semicolon", + /* 92 */ "keylabel_for_symbols_percent", + /* 93 */ "more_keys_for_symbols_exclamation", + /* 94 */ "more_keys_for_symbols_question", + /* 95 */ "more_keys_for_symbols_semicolon", + /* 96 */ "more_keys_for_symbols_percent", + /* 97 */ "keylabel_for_tablet_comma", + /* 98 */ "keyhintlabel_for_tablet_comma", + /* 99 */ "more_keys_for_tablet_comma", + /* 100 */ "keyhintlabel_for_tablet_period", + /* 101 */ "more_keys_for_tablet_period", + /* 102 */ "keylabel_for_apostrophe", + /* 103 */ "keyhintlabel_for_apostrophe", + /* 104 */ "more_keys_for_apostrophe", + /* 105 */ "more_keys_for_q", + /* 106 */ "more_keys_for_x", + /* 107 */ "keylabel_for_q", + /* 108 */ "keylabel_for_w", + /* 109 */ "keylabel_for_y", + /* 110 */ "keylabel_for_x", + /* 111 */ "keylabel_for_spanish_row2_10", + /* 112 */ "more_keys_for_am_pm", + /* 113 */ "settings_as_more_key", + /* 114 */ "shortcut_as_more_key", + /* 115 */ "action_next_as_more_key", + /* 116 */ "action_previous_as_more_key", + /* 117 */ "label_to_more_symbol_key", + /* 118 */ "label_to_more_symbol_for_tablet_key", + /* 119 */ "label_tab_key", + /* 120 */ "label_to_phone_numeric_key", + /* 121 */ "label_to_phone_symbols_key", + /* 122 */ "label_time_am", + /* 123 */ "label_time_pm", + /* 124 */ "label_to_symbol_key_pcqwerty", + /* 125 */ "keylabel_for_popular_domain", + /* 126 */ "more_keys_for_popular_domain", + /* 127 */ "more_keys_for_smiley", }; private static final String EMPTY = ""; @@ -258,26 +258,25 @@ public final class KeyboardTextsSet { // U+00A5: "¥" YEN SIGN // U+20B1: "₱" PESO SIGN /* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", - /* 46 */ "\u00A2,\u00A3,$,\u00A5,\u20B1", - /* 47 */ "\u00A2,$,\u20AC,\u00A5,\u20B1", - /* 48 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1", - /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", + /* 46 */ "$", + /* 47 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", + /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER // U+2605: "★" BLACK STAR - /* 50 */ "\u2020,\u2021,\u2605", + /* 49 */ "\u2020,\u2021,\u2605", // U+266A: "♪" EIGHTH NOTE // U+2665: "♥" BLACK HEART SUIT // U+2660: "♠" BLACK SPADE SUIT // U+2666: "♦" BLACK DIAMOND SUIT // U+2663: "♣" BLACK CLUB SUIT - /* 51 */ "\u266A,\u2665,\u2660,\u2666,\u2663", + /* 50 */ "\u266A,\u2665,\u2660,\u2666,\u2663", // U+00B1: "±" PLUS-MINUS SIGN - /* 52 */ "\u00B1", + /* 51 */ "\u00B1", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 53 */ "!fixedColumnOrder!3,<,{,[", - /* 54 */ "!fixedColumnOrder!3,>,},]", + /* 52 */ "!fixedColumnOrder!3,<,{,[", + /* 53 */ "!fixedColumnOrder!3,>,},]", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+2264: "≤" LESS-THAN OR EQUAL TO @@ -293,103 +292,103 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 55 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", - /* 56 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", + /* 54 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", + /* 55 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", + /* 56 */ EMPTY, /* 57 */ EMPTY, - /* 58 */ EMPTY, - /* 59 */ "1", - /* 60 */ "2", - /* 61 */ "3", - /* 62 */ "4", - /* 63 */ "5", - /* 64 */ "6", - /* 65 */ "7", - /* 66 */ "8", - /* 67 */ "9", - /* 68 */ "0", - /* 69~ */ + /* 58 */ "1", + /* 59 */ "2", + /* 60 */ "3", + /* 61 */ "4", + /* 62 */ "5", + /* 63 */ "6", + /* 64 */ "7", + /* 65 */ "8", + /* 66 */ "9", + /* 67 */ "0", + /* 68~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~78 */ + /* ~77 */ // U+00B9: "¹" SUPERSCRIPT ONE // U+00BD: "½" VULGAR FRACTION ONE HALF // U+2153: "⅓" VULGAR FRACTION ONE THIRD // U+00BC: "¼" VULGAR FRACTION ONE QUARTER // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH - /* 79 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", + /* 78 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", // U+00B2: "²" SUPERSCRIPT TWO // U+2154: "⅔" VULGAR FRACTION TWO THIRDS - /* 80 */ "\u00B2,\u2154", + /* 79 */ "\u00B2,\u2154", // U+00B3: "³" SUPERSCRIPT THREE // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS - /* 81 */ "\u00B3,\u00BE,\u215C", + /* 80 */ "\u00B3,\u00BE,\u215C", // U+2074: "⁴" SUPERSCRIPT FOUR - /* 82 */ "\u2074", + /* 81 */ "\u2074", // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS - /* 83 */ "\u215D", - /* 84 */ EMPTY, + /* 82 */ "\u215D", + /* 83 */ EMPTY, // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS - /* 85 */ "\u215E", + /* 84 */ "\u215E", + /* 85 */ EMPTY, /* 86 */ EMPTY, - /* 87 */ EMPTY, // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N // U+2205: "∅" EMPTY SET - /* 88 */ "\u207F,\u2205", - /* 89 */ ",", - /* 90 */ EMPTY, - /* 91 */ "?", - /* 92 */ ";", - /* 93 */ "%", + /* 87 */ "\u207F,\u2205", + /* 88 */ ",", + /* 89 */ EMPTY, + /* 90 */ "?", + /* 91 */ ";", + /* 92 */ "%", // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 94 */ "\u00A1", + /* 93 */ "\u00A1", // U+00BF: "¿" INVERTED QUESTION MARK - /* 95 */ "\u00BF", - /* 96 */ EMPTY, + /* 94 */ "\u00BF", + /* 95 */ EMPTY, // U+2030: "‰" PER MILLE SIGN - /* 97 */ "\u2030", - /* 98 */ ",", + /* 96 */ "\u2030", + /* 97 */ ",", + /* 98 */ "!", /* 99 */ "!", - /* 100 */ "!", + /* 100 */ "?", /* 101 */ "?", - /* 102 */ "?", - /* 103 */ "\'", + /* 102 */ "\'", + /* 103 */ "\"", /* 104 */ "\"", - /* 105 */ "\"", + /* 105 */ EMPTY, /* 106 */ EMPTY, - /* 107 */ EMPTY, - /* 108 */ "q", - /* 109 */ "w", - /* 110 */ "y", - /* 111 */ "x", + /* 107 */ "q", + /* 108 */ "w", + /* 109 */ "y", + /* 110 */ "x", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE - /* 112 */ "\u00F1", - /* 113 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", - /* 114 */ "!icon/settings_key|!code/key_settings", - /* 115 */ "!icon/shortcut_key|!code/key_shortcut", - /* 116 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", - /* 117 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", + /* 111 */ "\u00F1", + /* 112 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", + /* 113 */ "!icon/settings_key|!code/key_settings", + /* 114 */ "!icon/shortcut_key|!code/key_shortcut", + /* 115 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", + /* 116 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", // Label for "switch to more symbol" modifier key. Must be short to fit on key! - /* 118 */ "= \\ <", + /* 117 */ "= \\ <", // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! - /* 119 */ "~ \\ {", + /* 118 */ "~ \\ {", // Label for "Tab" key. Must be short to fit on key! - /* 120 */ "Tab", + /* 119 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! - /* 121 */ "123", + /* 120 */ "123", // Label for "switch to phone symbols" key. Must be short to fit on key! // U+FF0A: "*" FULLWIDTH ASTERISK // U+FF03: "#" FULLWIDTH NUMBER SIGN - /* 122 */ "\uFF0A\uFF03", + /* 121 */ "\uFF0A\uFF03", // Key label for "ante meridiem" - /* 123 */ "AM", + /* 122 */ "AM", // Key label for "post meridiem" - /* 124 */ "PM", + /* 123 */ "PM", // Label for "switch to symbols" key on PC QWERTY layout - /* 125 */ "Sym", - /* 126 */ ".com", + /* 124 */ "Sym", + /* 125 */ ".com", // popular web domains for the locale - most popular, displayed on the keyboard - /* 127 */ "!hasLabels!,.net,.org,.gov,.edu", - /* 128 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + /* 126 */ "!hasLabels!,.net,.org,.gov,.edu", + /* 127 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", }; /* Language af: Afrikaans */ @@ -458,25 +457,31 @@ public final class KeyboardTextsSet { // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«|»,»|«;,‘,’,‚,‛</string> /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", - /* 45~ */ - null, null, null, null, - /* ~48 */ + // U+00A2: "¢" CENT SIGN + // U+00A3: "£" POUND SIGN + // U+20AC: "€" EURO SIGN + // U+00A5: "¥" YEN SIGN + // U+20B1: "₱" PESO SIGN + // U+FDFC: "﷼" RIAL SIGN + /* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1,\uFDFC", + /* 46 */ null, + /* 47 */ null, // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 50 */ "\u2605,\u066D", + /* 49 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 51 */ "\u266A", - /* 52 */ null, + /* 50 */ "\u266A", + /* 51 */ null, // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS - /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -492,8 +497,8 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0654: "ٔ" ARABIC HAMZA ABOVE // U+0652: "ْ" ARABIC SUKUN @@ -509,64 +514,64 @@ public final class KeyboardTextsSet { // U+064E: "َ" ARABIC FATHA // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. - /* 57 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640", - /* 58 */ "\u0651", + /* 56 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640", + /* 57 */ "\u0651", // U+0661: "١" ARABIC-INDIC DIGIT ONE - /* 59 */ "\u0661", + /* 58 */ "\u0661", // U+0662: "٢" ARABIC-INDIC DIGIT TWO - /* 60 */ "\u0662", + /* 59 */ "\u0662", // U+0663: "٣" ARABIC-INDIC DIGIT THREE - /* 61 */ "\u0663", + /* 60 */ "\u0663", // U+0664: "٤" ARABIC-INDIC DIGIT FOUR - /* 62 */ "\u0664", + /* 61 */ "\u0664", // U+0665: "٥" ARABIC-INDIC DIGIT FIVE - /* 63 */ "\u0665", + /* 62 */ "\u0665", // U+0666: "٦" ARABIC-INDIC DIGIT SIX - /* 64 */ "\u0666", + /* 63 */ "\u0666", // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN - /* 65 */ "\u0667", + /* 64 */ "\u0667", // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT - /* 66 */ "\u0668", + /* 65 */ "\u0668", // U+0669: "٩" ARABIC-INDIC DIGIT NINE - /* 67 */ "\u0669", + /* 66 */ "\u0669", // U+0660: "٠" ARABIC-INDIC DIGIT ZERO - /* 68 */ "\u0660", - /* 69 */ "1", - /* 70 */ "2", - /* 71 */ "3", - /* 72 */ "4", - /* 73 */ "5", - /* 74 */ "6", - /* 75 */ "7", - /* 76 */ "8", - /* 77 */ "9", + /* 67 */ "\u0660", + /* 68 */ "1", + /* 69 */ "2", + /* 70 */ "3", + /* 71 */ "4", + /* 72 */ "5", + /* 73 */ "6", + /* 74 */ "7", + /* 75 */ "8", + /* 76 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 78 */ "0,\u066B,\u066C", - /* 79~ */ + /* 77 */ "0,\u066B,\u066C", + /* 78~ */ null, null, null, null, null, null, null, null, null, null, - /* ~88 */ + /* ~87 */ // U+060C: "،" ARABIC COMMA - /* 89 */ "\u060C", - /* 90 */ "\\,", - /* 91 */ "\u061F", - /* 92 */ "\u061B", + /* 88 */ "\u060C", + /* 89 */ "\\,", + /* 90 */ "\u061F", + /* 91 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 93 */ "\u066A", - /* 94 */ null, - /* 95 */ "?", - /* 96 */ ";", + /* 92 */ "\u066A", + /* 93 */ null, + /* 94 */ "?", + /* 95 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 97 */ "\\%,\u2030", - /* 98~ */ + /* 96 */ "\\%,\u2030", + /* 97~ */ null, null, null, null, null, - /* ~102 */ + /* ~101 */ // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK - /* 103 */ "\u060C", - /* 104 */ "\u061F", - /* 105 */ "\u061F,\u061B,!,:,-,/,\',\"", + /* 102 */ "\u060C", + /* 103 */ "\u061F", + /* 104 */ "\u061F,\u061B,!,:,-,/,\',\"", }; /* Language be: Belarusian */ @@ -998,20 +1003,20 @@ public final class KeyboardTextsSet { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - /* ~105 */ - /* 106 */ "q", - /* 107 */ "x", + null, null, null, null, null, null, null, null, null, null, + /* ~104 */ + /* 105 */ "q", + /* 106 */ "x", // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX - /* 108 */ "\u015D", + /* 107 */ "\u015D", // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX - /* 109 */ "\u011D", + /* 108 */ "\u011D", // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE - /* 110 */ "\u016D", + /* 109 */ "\u016D", // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX - /* 111 */ "\u0109", + /* 110 */ "\u0109", // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX - /* 112 */ "\u0135", + /* 111 */ "\u0135", }; /* Language es: Spanish */ @@ -1069,22 +1074,25 @@ public final class KeyboardTextsSet { /* 8~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - /* ~48 */ + null, null, null, null, null, null, null, null, null, null, + /* ~47 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK - /* 49 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", - /* 50~ */ + /* 48 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)", + /* 49~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~99 */ + /* ~98 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 100 */ "!,\u00A1", - /* 101 */ null, + /* 99 */ "!,\u00A1", + /* 100 */ null, // U+00BF: "¿" INVERTED QUESTION MARK - /* 102 */ "?,\u00BF", + /* 101 */ "?,\u00BF", + /* 102 */ "\"", + /* 103 */ "\'", + /* 104 */ "\'", }; /* Language et: Estonian */ @@ -1200,25 +1208,32 @@ public final class KeyboardTextsSet { // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«|»,»|«;,‘,’,‚,‛</string> /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", - /* 45~ */ - null, null, null, null, - /* ~48 */ + /* 45 */ null, + // U+FDFC: "﷼" RIAL SIGN + // U+060B: "؋" AFGHANI SIGN + // U+00A2: "¢" CENT SIGN + // U+00A3: "£" POUND SIGN + // U+20AC: "€" EURO SIGN + // U+00A5: "¥" YEN SIGN + // U+20B1: "₱" PESO SIGN + /* 46 */ "\uFDFC", + /* 47 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1,\u060B", // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 50 */ "\u2605,\u066D", + /* 49 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 51 */ "\u266A", - /* 52 */ null, + /* 50 */ "\u266A", + /* 51 */ null, // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS - /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -1234,8 +1249,8 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", - /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", + /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", + /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0652: "ْ" ARABIC SUKUN // U+0651: "ّ" ARABIC SHADDA @@ -1251,68 +1266,68 @@ public final class KeyboardTextsSet { // U+064E: "َ" ARABIC FATHA // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. - /* 57 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640", - /* 58 */ "\u064B", + /* 56 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640", + /* 57 */ "\u064B", // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE - /* 59 */ "\u06F1", + /* 58 */ "\u06F1", // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO - /* 60 */ "\u06F2", + /* 59 */ "\u06F2", // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE - /* 61 */ "\u06F3", + /* 60 */ "\u06F3", // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR - /* 62 */ "\u06F4", + /* 61 */ "\u06F4", // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE - /* 63 */ "\u06F5", + /* 62 */ "\u06F5", // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX - /* 64 */ "\u06F6", + /* 63 */ "\u06F6", // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN - /* 65 */ "\u06F7", + /* 64 */ "\u06F7", // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT - /* 66 */ "\u06F8", + /* 65 */ "\u06F8", // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE - /* 67 */ "\u06F9", + /* 66 */ "\u06F9", // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO - /* 68 */ "\u06F0", - /* 69 */ "1", - /* 70 */ "2", - /* 71 */ "3", - /* 72 */ "4", - /* 73 */ "5", - /* 74 */ "6", - /* 75 */ "7", - /* 76 */ "8", - /* 77 */ "9", + /* 67 */ "\u06F0", + /* 68 */ "1", + /* 69 */ "2", + /* 70 */ "3", + /* 71 */ "4", + /* 72 */ "5", + /* 73 */ "6", + /* 74 */ "7", + /* 75 */ "8", + /* 76 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 78 */ "0,\u066B,\u066C", - /* 79~ */ + /* 77 */ "0,\u066B,\u066C", + /* 78~ */ null, null, null, null, null, null, null, null, null, null, - /* ~88 */ + /* ~87 */ // U+060C: "،" ARABIC COMMA - /* 89 */ "\u060C", - /* 90 */ "\\,", - /* 91 */ "\u061F", - /* 92 */ "\u061B", + /* 88 */ "\u060C", + /* 89 */ "\\,", + /* 90 */ "\u061F", + /* 91 */ "\u061B", // U+066A: "٪" ARABIC PERCENT SIGN - /* 93 */ "\u066A", - /* 94 */ null, - /* 95 */ "?", - /* 96 */ ";", + /* 92 */ "\u066A", + /* 93 */ null, + /* 94 */ "?", + /* 95 */ ";", // U+2030: "‰" PER MILLE SIGN - /* 97 */ "\\%,\u2030", + /* 96 */ "\\%,\u2030", // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - /* 98 */ "\u060C", - /* 99 */ "!", - /* 100 */ "!,\\,", - /* 101 */ "\u061F", - /* 102 */ "\u061F,?", - /* 103 */ "\u060C", - /* 104 */ "\u061F", - /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", + /* 97 */ "\u060C", + /* 98 */ "!", + /* 99 */ "!,\\,", + /* 100 */ "\u061F", + /* 101 */ "\u061F,?", + /* 102 */ "\u060C", + /* 103 */ "\u061F", + /* 104 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", }; /* Language fi: Finnish */ @@ -1421,38 +1436,43 @@ public final class KeyboardTextsSet { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~58 */ + null, + /* ~45 */ + // U+20B9: "₹" INDIAN RUPEE SIGN + /* 46 */ "\u20B9", + /* 47~ */ + null, null, null, null, null, null, null, null, null, null, null, + /* ~57 */ // U+0967: "१" DEVANAGARI DIGIT ONE - /* 59 */ "\u0967", + /* 58 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO - /* 60 */ "\u0968", + /* 59 */ "\u0968", // U+0969: "३" DEVANAGARI DIGIT THREE - /* 61 */ "\u0969", + /* 60 */ "\u0969", // U+096A: "४" DEVANAGARI DIGIT FOUR - /* 62 */ "\u096A", + /* 61 */ "\u096A", // U+096B: "५" DEVANAGARI DIGIT FIVE - /* 63 */ "\u096B", + /* 62 */ "\u096B", // U+096C: "६" DEVANAGARI DIGIT SIX - /* 64 */ "\u096C", + /* 63 */ "\u096C", // U+096D: "७" DEVANAGARI DIGIT SEVEN - /* 65 */ "\u096D", + /* 64 */ "\u096D", // U+096E: "८" DEVANAGARI DIGIT EIGHT - /* 66 */ "\u096E", + /* 65 */ "\u096E", // U+096F: "९" DEVANAGARI DIGIT NINE - /* 67 */ "\u096F", + /* 66 */ "\u096F", // U+0966: "०" DEVANAGARI DIGIT ZERO - /* 68 */ "\u0966", - /* 69 */ "1", - /* 70 */ "2", - /* 71 */ "3", - /* 72 */ "4", - /* 73 */ "5", - /* 74 */ "6", - /* 75 */ "7", - /* 76 */ "8", - /* 77 */ "9", - /* 78 */ "0", + /* 67 */ "\u0966", + /* 68 */ "1", + /* 69 */ "2", + /* 70 */ "3", + /* 71 */ "4", + /* 72 */ "5", + /* 73 */ "6", + /* 74 */ "7", + /* 75 */ "8", + /* 76 */ "9", + /* 77 */ "0", }; /* Language hr: Croatian */ @@ -1648,19 +1668,21 @@ public final class KeyboardTextsSet { // TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK // <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,“,”,„,‟,«|»,»|«;,‘,’,‚,‛</string> /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B", - /* 45~ */ - null, null, null, null, null, - /* ~49 */ + /* 45 */ null, + // U+20AA: "₪" NEW SHEQEL SIGN + /* 46 */ "\u20AA", + /* 47 */ null, + /* 48 */ null, // U+2605: "★" BLACK STAR - /* 50 */ "\u2605", - /* 51 */ null, + /* 49 */ "\u2605", + /* 50 */ null, // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN - /* 52 */ "\u00B1,\uFB29", + /* 51 */ "\u00B1,\uFB29", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 53 */ "!fixedColumnOrder!3,<|>,{|},[|]", - /* 54 */ "!fixedColumnOrder!3,>|<,}|{,]|[", + /* 52 */ "!fixedColumnOrder!3,<|>,{|},[|]", + /* 53 */ "!fixedColumnOrder!3,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -1676,8 +1698,8 @@ public final class KeyboardTextsSet { // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK // U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK - /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", }; /* Language ky: Kirghiz */ @@ -1928,6 +1950,18 @@ public final class KeyboardTextsSet { /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B", }; + /* Language mn: Mongolian */ + private static final String[] LANGUAGE_mn = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, + /* ~45 */ + // U+20AE: "₮" TUGRIK SIGN + /* 46 */ "\u20AE", + }; + /* Language nb: Norwegian Bokmål */ private static final String[] LANGUAGE_nb = { // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -2465,6 +2499,18 @@ public final class KeyboardTextsSet { /* 15 */ "g\'", }; + /* Language th: Thai */ + private static final String[] LANGUAGE_th = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, + /* ~45 */ + // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT + /* 46 */ "\u0E3F", + }; + /* Language tl: Tagalog */ private static final String[] LANGUAGE_tl = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -2589,6 +2635,11 @@ public final class KeyboardTextsSet { /* 34 */ null, // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN /* 35 */ "\u044A", + /* 36~ */ + null, null, null, null, null, null, null, null, null, null, + /* ~45 */ + // U+20B4: "₴" HRYVNIA SIGN + /* 46 */ "\u20B4", }; /* Language vi: Vietnamese */ @@ -2670,6 +2721,13 @@ public final class KeyboardTextsSet { /* 8 */ "\u1EF3,\u00FD,\u1EF7,\u1EF9,\u1EF5", // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE /* 9 */ "\u0111", + /* 10~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, + /* ~45 */ + // U+20AB: "₫" DONG SIGN + /* 46 */ "\u20AB", }; /* Language zu: Zulu */ @@ -2868,6 +2926,7 @@ public final class KeyboardTextsSet { "lt", LANGUAGE_lt, /* Lithuanian */ "lv", LANGUAGE_lv, /* Latvian */ "mk", LANGUAGE_mk, /* Macedonian */ + "mn", LANGUAGE_mn, /* Mongolian */ "nb", LANGUAGE_nb, /* Norwegian Bokmål */ "nl", LANGUAGE_nl, /* Dutch */ "pl", LANGUAGE_pl, /* Polish */ @@ -2880,6 +2939,7 @@ public final class KeyboardTextsSet { "sr", LANGUAGE_sr, /* Serbian */ "sv", LANGUAGE_sv, /* Swedish */ "sw", LANGUAGE_sw, /* Swahili */ + "th", LANGUAGE_th, /* Thai */ "tl", LANGUAGE_tl, /* Tagalog */ "tr", LANGUAGE_tr, /* Turkish */ "uk", LANGUAGE_uk, /* Ukrainian */ diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java index 550391b49..ca16163d4 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java @@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.internal; import android.text.TextUtils; -import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.StringUtils; import java.util.Locale; @@ -35,10 +35,10 @@ public final class MoreKeySpec { KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale); final int code = KeySpecParser.toUpperCaseOfCodeForLocale( KeySpecParser.getCode(moreKeySpec, codesSet), needsToUpperCase, locale); - if (code == Keyboard.CODE_UNSPECIFIED) { + if (code == Constants.CODE_UNSPECIFIED) { // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters // upper case representation ("SS"). - mCode = Keyboard.CODE_OUTPUT_TEXT; + mCode = Constants.CODE_OUTPUT_TEXT; mOutputText = mLabel; } else { mCode = code; @@ -75,8 +75,8 @@ public final class MoreKeySpec { public String toString() { final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel : KeySpecParser.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId)); - final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText - : Keyboard.printableCode(mCode)); + final String output = (mCode == Constants.CODE_OUTPUT_TEXT ? mOutputText + : Constants.printableCode(mCode)); if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) { return output; } else { diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java index d8950a713..d7a2b6f39 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard.internal; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.LatinImeLogger; public final class TouchPositionCorrection { @@ -66,7 +67,7 @@ public final class TouchPositionCorrection { } } - // For test only + @UsedForTesting public void setEnabled(final boolean enabled) { mEnabled = enabled; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java index c53428fe5..c795d5322 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java @@ -20,7 +20,6 @@ import android.content.Context; import android.util.Log; import android.view.MotionEvent; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -28,8 +27,8 @@ import com.android.inputmethod.latin.ResourceUtils; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.research.ResearchLogger; -public final class SuddenJumpingTouchEventHandler { - private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName(); +public final class TouchScreenRegulator { + private static final String TAG = TouchScreenRegulator.class.getSimpleName(); private static boolean DEBUG_MODE = LatinImeLogger.sDBG; public interface ProcessMotionEvent { @@ -50,17 +49,18 @@ public final class SuddenJumpingTouchEventHandler { private int mJumpThresholdSquare = Integer.MAX_VALUE; private int mLastX; private int mLastY; + // One-seventh of the keyboard width seems like a reasonable threshold + private static final float JUMP_THRESHOLD_RATIO_TO_KEYBOARD_WIDTH = 1.0f / 7.0f; - public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) { + public TouchScreenRegulator(final Context context, final ProcessMotionEvent view) { mView = view; mNeedsSuddenJumpingHack = Boolean.parseBoolean(ResourceUtils.getDeviceOverrideValue( context.getResources(), R.array.sudden_jumping_touch_event_device_list, "false")); } - public void setKeyboard(Keyboard newKeyboard) { - // One-seventh of the keyboard width seems like a reasonable threshold - final int jumpThreshold = newKeyboard.mOccupiedWidth / 7; - mJumpThresholdSquare = jumpThreshold * jumpThreshold; + public void setKeyboardGeometry(final int keyboardWidth) { + final float jumpThreshold = keyboardWidth * JUMP_THRESHOLD_RATIO_TO_KEYBOARD_WIDTH; + mJumpThresholdSquare = (int)(jumpThreshold * jumpThreshold); } /** @@ -74,7 +74,7 @@ public final class SuddenJumpingTouchEventHandler { * @return true if the event was consumed, so that it doesn't continue to be handled by * {@link MainKeyboardView}. */ - private boolean handleSuddenJumping(MotionEvent me) { + private boolean handleSuddenJumping(final MotionEvent me) { if (!mNeedsSuddenJumpingHack) return false; final int action = me.getAction(); @@ -140,7 +140,7 @@ public final class SuddenJumpingTouchEventHandler { return result; } - public boolean onTouchEvent(MotionEvent me) { + public boolean onTouchEvent(final MotionEvent me) { // If there was a sudden jump, return without processing the actual motion event. if (handleSuddenJumping(me)) { if (DEBUG_MODE) diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java index 939451899..96c08b3cd 100644 --- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java @@ -50,6 +50,7 @@ import java.util.ArrayList; import java.util.TreeSet; public final class AdditionalSubtypeSettings extends PreferenceFragment { + private RichInputMethodManager mRichImm; private SharedPreferences mPrefs; private SubtypeLocaleAdapter mSubtypeLocaleAdapter; private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; @@ -63,6 +64,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN = "is_subtype_enabler_notification_dialog_open"; private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler"; + static final class SubtypeLocaleItem extends Pair<String, String> implements Comparable<SubtypeLocaleItem> { public SubtypeLocaleItem(final String localeString, final String displayName) { @@ -93,7 +95,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet(); - final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context); + final InputMethodInfo imi = RichInputMethodManager.getInstance() + .getInputMethodInfoOfThisIme(); final int count = imi.getSubtypeCount(); for (int i = 0; i < count; i++) { final InputMethodSubtype subtype = imi.getSubtypeAt(i); @@ -383,6 +386,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + RichInputMethodManager.init(getActivity()); + mRichImm = RichInputMethodManager.getInstance(); addPreferencesFromResource(R.xml.additional_subtype_settings); setHasOptionsMenu(true); @@ -439,7 +444,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { mIsAddingNewSubtype = false; final PreferenceGroup group = getPreferenceScreen(); group.removePreference(subtypePref); - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); } @Override @@ -449,7 +454,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { return; } if (findDuplicatedSubtype(subtype) == null) { - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); return; } @@ -466,7 +471,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { mIsAddingNewSubtype = false; final InputMethodSubtype subtype = subtypePref.getSubtype(); if (findDuplicatedSubtype(subtype) == null) { - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); mSubtypeEnablerNotificationDialog = createDialog(subtypePref); mSubtypeEnablerNotificationDialog.show(); @@ -501,8 +506,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) { final String localeString = subtype.getLocale(); final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype); - return ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - getActivity(), localeString, keyboardLayoutSetName); + return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + localeString, keyboardLayoutSetName); } private AlertDialog createDialog( @@ -515,7 +520,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { @Override public void onClick(DialogInterface dialog, int which) { final Intent intent = CompatUtils.getInputLanguageSelectionIntent( - ImfUtils.getInputMethodIdOfThisIme(getActivity()), + mRichImm.getInputMethodIdOfThisIme(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -573,7 +578,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { } finally { editor.apply(); } - ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), subtypes); + mRichImm.setAdditionalInputMethodSubtypes(subtypes); } @Override diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java index 59ef5e09f..024726391 100644 --- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java +++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java @@ -21,7 +21,6 @@ import android.media.AudioManager; import android.view.HapticFeedbackConstants; import android.view.View; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.VibratorUtils; /** @@ -31,17 +30,15 @@ import com.android.inputmethod.latin.VibratorUtils; * complexity of settings and the like. */ public final class AudioAndHapticFeedbackManager { - final private SettingsValues mSettingsValues; - final private AudioManager mAudioManager; - final private VibratorUtils mVibratorUtils; + private final AudioManager mAudioManager; + private final VibratorUtils mVibratorUtils; + + private SettingsValues mSettingsValues; private boolean mSoundOn; - public AudioAndHapticFeedbackManager(final LatinIME latinIme, - final SettingsValues settingsValues) { - mSettingsValues = settingsValues; + public AudioAndHapticFeedbackManager(final LatinIME latinIme) { mVibratorUtils = VibratorUtils.getInstance(latinIme); mAudioManager = (AudioManager) latinIme.getSystemService(Context.AUDIO_SERVICE); - mSoundOn = reevaluateIfSoundIsOn(); } public void hapticAndAudioFeedback(final int primaryCode, @@ -51,7 +48,7 @@ public final class AudioAndHapticFeedbackManager { } private boolean reevaluateIfSoundIsOn() { - if (!mSettingsValues.mSoundOn || mAudioManager == null) { + if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) { return false; } else { return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL; @@ -64,13 +61,13 @@ public final class AudioAndHapticFeedbackManager { if (mSoundOn) { final int sound; switch (primaryCode) { - case Keyboard.CODE_DELETE: + case Constants.CODE_DELETE: sound = AudioManager.FX_KEYPRESS_DELETE; break; - case Keyboard.CODE_ENTER: + case Constants.CODE_ENTER: sound = AudioManager.FX_KEYPRESS_RETURN; break; - case Keyboard.CODE_SPACE: + case Constants.CODE_SPACE: sound = AudioManager.FX_KEYPRESS_SPACEBAR; break; default: @@ -81,8 +78,7 @@ public final class AudioAndHapticFeedbackManager { } } - // TODO: make this private when LatinIME does not call it any more - public void vibrate(final View viewToPerformHapticFeedbackOn) { + private void vibrate(final View viewToPerformHapticFeedbackOn) { if (!mSettingsValues.mVibrateOn) { return; } @@ -98,6 +94,11 @@ public final class AudioAndHapticFeedbackManager { } } + public void onSettingsChanged(final SettingsValues settingsValues) { + mSettingsValues = settingsValues; + mSoundOn = reevaluateIfSoundIsOn(); + } + public void onRingerModeChanged() { mSoundOn = reevaluateIfSoundIsOn(); } diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java index 84fad158f..fa35922b0 100644 --- a/java/src/com/android/inputmethod/latin/AutoCorrection.java +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -33,11 +33,11 @@ public final class AutoCorrection { } public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries, - CharSequence word, boolean ignoreCase) { + final String word, final boolean ignoreCase) { if (TextUtils.isEmpty(word)) { return false; } - final CharSequence lowerCasedWord = word.toString().toLowerCase(); + final String lowerCasedWord = word.toLowerCase(); for (final String key : dictionaries.keySet()) { final Dictionary dictionary = dictionaries.get(key); // It's unclear how realistically 'dictionary' can be null, but the monkey is somehow @@ -57,7 +57,7 @@ public final class AutoCorrection { } public static int getMaxFrequency(final ConcurrentHashMap<String, Dictionary> dictionaries, - CharSequence word) { + final String word) { if (TextUtils.isEmpty(word)) { return Dictionary.NOT_A_PROBABILITY; } @@ -76,12 +76,13 @@ public final class AutoCorrection { // Returns true if this is in any of the dictionaries. public static boolean isInTheDictionary( final ConcurrentHashMap<String, Dictionary> dictionaries, - final CharSequence word, final boolean ignoreCase) { + final String word, final boolean ignoreCase) { return isValidWord(dictionaries, word, ignoreCase); } - public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion, - CharSequence consideredWord, float autoCorrectionThreshold) { + public static boolean suggestionExceedsAutoCorrectionThreshold( + final SuggestedWordInfo suggestion, final String consideredWord, + final float autoCorrectionThreshold) { if (null != suggestion) { // Shortlist a whitelisted word if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true; @@ -89,8 +90,7 @@ public final class AutoCorrection { // TODO: when the normalized score of the first suggestion is nearly equals to // the normalized score of the second suggestion, behave less aggressive. final float normalizedScore = BinaryDictionary.calcNormalizedScore( - consideredWord.toString(), suggestion.mWord.toString(), - autoCorrectionSuggestionScore); + consideredWord, suggestion.mWord, autoCorrectionSuggestionScore); if (DBG) { Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + "," + autoCorrectionSuggestionScore + ", " + normalizedScore @@ -100,8 +100,7 @@ public final class AutoCorrection { if (DBG) { Log.d(TAG, "Auto corrected by S-threshold."); } - return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(), - suggestion.mWord); + return !shouldBlockAutoCorrectionBySafetyNet(consideredWord, suggestion.mWord); } } return false; @@ -110,7 +109,7 @@ public final class AutoCorrection { // TODO: Resolve the inconsistencies between the native auto correction algorithms and // this safety net public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord, - final CharSequence suggestion) { + final String suggestion) { // Safety net for auto correction. // Actually if we hit this safety net, it's a bug. // If user selected aggressive auto correction mode, there is no need to use the safety @@ -123,7 +122,7 @@ public final class AutoCorrection { } final int maxEditDistanceOfNativeDictionary = (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1; - final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString()); + final int distance = BinaryDictionary.editDistance(typedWord, suggestion); if (DBG) { Log.d(TAG, "Autocorrected edit distance = " + distance + ", " + maxEditDistanceOfNativeDictionary); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 7184f1d8a..6048a3308 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -31,7 +31,7 @@ import java.util.Locale; * Implements a static, compacted, binary dictionary of standard words. */ public final class BinaryDictionary extends Dictionary { - + private static final String TAG = BinaryDictionary.class.getSimpleName(); public static final String DICTIONARY_PACK_AUTHORITY = "com.android.inputmethod.latin.dictionarypack"; @@ -45,17 +45,13 @@ public final class BinaryDictionary extends Dictionary { public static final int MAX_WORDS = 18; public static final int MAX_SPACES = 16; - private static final String TAG = BinaryDictionary.class.getSimpleName(); private static final int MAX_PREDICTIONS = 60; private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS); - private static final int TYPED_LETTER_MULTIPLIER = 2; - private long mNativeDict; private final Locale mLocale; private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; - // TODO: The below should be int[] mOutputCodePoints - private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS]; + private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; private final int[] mSpaceIndices = new int[MAX_SPACES]; private final int[] mOutputScores = new int[MAX_RESULTS]; private final int[] mOutputTypes = new int[MAX_RESULTS]; @@ -67,7 +63,7 @@ public final class BinaryDictionary extends Dictionary { // TODO: There should be a way to remove used DicTraverseSession objects from // {@code mDicTraverseSessions}. - private DicTraverseSession getTraverseSession(int traverseSessionId) { + private DicTraverseSession getTraverseSession(final int traverseSessionId) { synchronized(mDicTraverseSessions) { DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); if (traverseSession == null) { @@ -84,7 +80,6 @@ public final class BinaryDictionary extends Dictionary { /** * Constructor for the binary dictionary. This is supposed to be called from the * dictionary factory. - * All implementations should pass null into flagArray, except for testing purposes. * @param context the context to access the environment from. * @param filename the name of the file to read through native code. * @param offset the offset of the dictionary data within the file. @@ -92,9 +87,9 @@ public final class BinaryDictionary extends Dictionary { * @param useFullEditDistance whether to use the full edit distance in suggestions * @param dictType the dictionary type, as a human-readable string */ - public BinaryDictionary(final Context context, - final String filename, final long offset, final long length, - final boolean useFullEditDistance, final Locale locale, final String dictType) { + public BinaryDictionary(final Context context, final String filename, final long offset, + final long length, final boolean useFullEditDistance, final Locale locale, + final String dictType) { super(dictType); mLocale = locale; mUseFullEditDistance = useFullEditDistance; @@ -106,40 +101,40 @@ public final class BinaryDictionary extends Dictionary { } private native long openNative(String sourceDir, long dictOffset, long dictSize, - int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, - int maxPredictions); + int maxWordLength, int maxWords, int maxPredictions); private native void closeNative(long dict); private native int getFrequencyNative(long dict, int[] word); private native boolean isValidBigramNative(long dict, int[] word1, int[] word2); private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodePoints, int codesSize, int commitPoint, boolean isGesture, - int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars, + int[] prevWordCodePointArray, boolean useFullEditDistance, int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes); - private static native float calcNormalizedScoreNative(char[] before, char[] after, int score); - private static native int editDistanceNative(char[] before, char[] after); + private static native float calcNormalizedScoreNative(int[] before, int[] after, int score); + private static native int editDistanceNative(int[] before, int[] after); // TODO: Move native dict into session - private final void loadDictionary(String path, long startOffset, long length) { - mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER, - FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS); + private final void loadDictionary(final String path, final long startOffset, + final long length) { + mNativeDict = openNative(path, startOffset, length, MAX_WORD_LENGTH, MAX_WORDS, + MAX_PREDICTIONS); } @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { + final String prevWord, final ProximityInfo proximityInfo) { return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0); } @Override public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) { + final String prevWord, final ProximityInfo proximityInfo, int sessionId) { if (!isValidDictionary()) return null; Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code final int[] prevWordCodePointArray = (null == prevWord) - ? null : StringUtils.toCodePointArray(prevWord.toString()); + ? null : StringUtils.toCodePointArray(prevWord); final int composerSize = composer.size(); final boolean isGesture = composer.isBatchMode(); @@ -157,7 +152,8 @@ public final class BinaryDictionary extends Dictionary { proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray, - mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes); + mUseFullEditDistance, mOutputCodePoints, mOutputScores, mSpaceIndices, + mOutputTypes); final int count = Math.min(tmpCount, MAX_PREDICTIONS); final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); @@ -165,53 +161,56 @@ public final class BinaryDictionary extends Dictionary { if (composerSize > 0 && mOutputScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; int len = 0; - while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { + while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) { ++len; } if (len > 0) { final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j] ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j]; - suggestions.add(new SuggestedWordInfo( - new String(mOutputChars, start, len), score, mOutputTypes[j], mDictType)); + suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len), + score, mOutputTypes[j], mDictType)); } } return suggestions; } - /* package for test */ boolean isValidDictionary() { + public boolean isValidDictionary() { return mNativeDict != 0; } - public static float calcNormalizedScore(String before, String after, int score) { - return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score); + public static float calcNormalizedScore(final String before, final String after, + final int score) { + return calcNormalizedScoreNative(StringUtils.toCodePointArray(before), + StringUtils.toCodePointArray(after), score); } - public static int editDistance(String before, String after) { + public static int editDistance(final String before, final String after) { if (before == null || after == null) { throw new IllegalArgumentException(); } - return editDistanceNative(before.toCharArray(), after.toCharArray()); + return editDistanceNative(StringUtils.toCodePointArray(before), + StringUtils.toCodePointArray(after)); } @Override - public boolean isValidWord(CharSequence word) { + public boolean isValidWord(final String word) { return getFrequency(word) >= 0; } @Override - public int getFrequency(CharSequence word) { + public int getFrequency(final String word) { if (word == null) return -1; - int[] codePoints = StringUtils.toCodePointArray(word.toString()); + int[] codePoints = StringUtils.toCodePointArray(word); return getFrequencyNative(mNativeDict, codePoints); } // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni // calls when checking for changes in an entire dictionary. - public boolean isValidBigram(CharSequence word1, CharSequence word2) { + public boolean isValidBigram(final String word1, final String word2) { if (TextUtils.isEmpty(word1) || TextUtils.isEmpty(word2)) return false; - int[] chars1 = StringUtils.toCodePointArray(word1.toString()); - int[] chars2 = StringUtils.toCodePointArray(word2.toString()); - return isValidBigramNative(mNativeDict, chars1, chars2); + final int[] codePoints1 = StringUtils.toCodePointArray(word1); + final int[] codePoints2 = StringUtils.toCodePointArray(word2); + return isValidBigramNative(mNativeDict, codePoints1, codePoints2); } @Override diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 9a3f88f52..5eab292fc 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -149,7 +149,13 @@ public final class BinaryDictionaryFileDumper { final Uri.Builder wordListUriBuilder = getProviderUriBuilder(id); final String finalFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context); - final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context); + String tempFileName; + try { + tempFileName = BinaryDictionaryGetter.getTempFileName(id, context); + } catch (IOException e) { + Log.e(TAG, "Can't open the temporary file", e); + return null; + } for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) { InputStream originalSourceStream = null; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index c747dc673..83dabbede 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; import com.android.inputmethod.latin.makedict.FormatSpec; @@ -165,14 +166,9 @@ final class BinaryDictionaryGetter { /** * Generates a unique temporary file name in the app cache directory. - * - * This is unique as long as it doesn't get called twice in the same millisecond by the same - * thread, which should be more than enough for our purposes. */ - public static String getTempFileName(String id, Context context) { - final String fileName = replaceFileNameDangerousCharacters(id); - return context.getCacheDir() + File.separator + fileName + "." - + Thread.currentThread().getId() + "." + System.currentTimeMillis(); + public static String getTempFileName(String id, Context context) throws IOException { + return File.createTempFile(replaceFileNameDangerousCharacters(id), null).getAbsolutePath(); } /** @@ -427,8 +423,11 @@ final class BinaryDictionaryGetter { // cacheWordListsFromContentProvider 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.cacheWordListsFromContentProvider(locale, context, - hasDefaultWordList); + // TODO: The experimental version is not supported by the Dictionary Pack Service yet + if (!ProductionFlag.IS_EXPERIMENTAL) { + BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context, + hasDefaultWordList); + } final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); final String mainDictId = getMainDictId(locale); final DictPackSettings dictPackSettings = new DictPackSettings(context); diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 57e12a64f..ba932e590 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -136,11 +136,81 @@ public final class Constants { public static final int NOT_A_CODE = -1; - // See {@link KeyboardActionListener.Adapter#isInvalidCoordinate(int)}. public static final int NOT_A_COORDINATE = -1; public static final int SUGGESTION_STRIP_COORDINATE = -2; public static final int SPELL_CHECKER_COORDINATE = -3; + public static boolean isValidCoordinate(final int coordinate) { + // Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE}, + // and {@link SPELL_CHECKER_COORDINATE}. + return coordinate >= 0; + } + + /** + * Some common keys code. Must be positive. + */ + public static final int CODE_ENTER = '\n'; + public static final int CODE_TAB = '\t'; + public static final int CODE_SPACE = ' '; + public static final int CODE_PERIOD = '.'; + public static final int CODE_DASH = '-'; + public static final int CODE_SINGLE_QUOTE = '\''; + public static final int CODE_DOUBLE_QUOTE = '"'; + public static final int CODE_QUESTION_MARK = '?'; + public static final int CODE_EXCLAMATION_MARK = '!'; + // TODO: Check how this should work for right-to-left languages. It seems to stand + // that for rtl languages, a closing parenthesis is a left parenthesis. Is this + // managed by the font? Or is it a different char? + public static final int CODE_CLOSING_PARENTHESIS = ')'; + public static final int CODE_CLOSING_SQUARE_BRACKET = ']'; + public static final int CODE_CLOSING_CURLY_BRACKET = '}'; + public static final int CODE_CLOSING_ANGLE_BRACKET = '>'; + + /** + * Special keys code. Must be negative. + * These should be aligned with KeyboardCodesSet.ID_TO_NAME[], + * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[] + */ + public static final int CODE_SHIFT = -1; + public static final int CODE_SWITCH_ALPHA_SYMBOL = -2; + public static final int CODE_OUTPUT_TEXT = -3; + public static final int CODE_DELETE = -4; + public static final int CODE_SETTINGS = -5; + public static final int CODE_SHORTCUT = -6; + public static final int CODE_ACTION_ENTER = -7; + public static final int CODE_ACTION_NEXT = -8; + public static final int CODE_ACTION_PREVIOUS = -9; + public static final int CODE_LANGUAGE_SWITCH = -10; + public static final int CODE_RESEARCH = -11; + // Code value representing the code is not specified. + public static final int CODE_UNSPECIFIED = -12; + + public static boolean isLetterCode(final int code) { + return code >= CODE_SPACE; + } + + public static String printableCode(final int code) { + switch (code) { + case CODE_SHIFT: return "shift"; + case CODE_SWITCH_ALPHA_SYMBOL: return "symbol"; + case CODE_OUTPUT_TEXT: return "text"; + case CODE_DELETE: return "delete"; + case CODE_SETTINGS: return "settings"; + case CODE_SHORTCUT: return "shortcut"; + case CODE_ACTION_ENTER: return "actionEnter"; + case CODE_ACTION_NEXT: return "actionNext"; + case CODE_ACTION_PREVIOUS: return "actionPrevious"; + case CODE_LANGUAGE_SWITCH: return "languageSwitch"; + case CODE_UNSPECIFIED: return "unspec"; + case CODE_TAB: return "tab"; + case CODE_ENTER: return "enter"; + default: + if (code < CODE_SPACE) return String.format("'\\u%02x'", code); + if (code < 0x100) return String.format("'%c'", code); + return String.format("'\\u%04x'", code); + } + } + private Constants() { // This utility class is not publicly instantiable. } diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 5edc4314f..d1b32a2f3 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -24,8 +24,6 @@ import android.provider.ContactsContract.Contacts; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.keyboard.Keyboard; - import java.util.Locale; public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { @@ -62,7 +60,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { */ private final boolean mUseFirstLastBigrams; - public ContactsBinaryDictionary(final Context context, Locale locale) { + public ContactsBinaryDictionary(final Context context, final Locale locale) { super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS); mLocale = locale; mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); @@ -120,7 +118,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } } - private boolean useFirstLastBigramsForLocale(Locale locale) { + private boolean useFirstLastBigramsForLocale(final Locale locale) { // TODO: Add firstname/lastname bigram rules for other languages. if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { return true; @@ -128,7 +126,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { return false; } - private void addWords(Cursor cursor) { + private void addWords(final Cursor cursor) { clearFusionDictionary(); int count = 0; while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) { @@ -160,7 +158,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { * Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their * bigrams depending on locale. */ - private void addName(String name) { + private void addName(final String name) { int len = StringUtils.codePointCount(name); String prevWord = null; // TODO: Better tokenization for non-Latin writing systems @@ -188,12 +186,13 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { /** * Returns the index of the last letter in the word, starting from position startIndex. */ - private static int getWordEndPosition(String string, int len, int startIndex) { + private static int getWordEndPosition(final String string, final int len, + final int startIndex) { int end; int cp = 0; for (end = startIndex + 1; end < len; end += Character.charCount(cp)) { cp = string.codePointAt(end); - if (!(cp == Keyboard.CODE_DASH || cp == Keyboard.CODE_SINGLE_QUOTE + if (!(cp == Constants.CODE_DASH || cp == Constants.CODE_SINGLE_QUOTE || Character.isLetter(cp))) { break; } @@ -249,7 +248,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { return false; } - private static boolean isValidName(String name) { + private static boolean isValidName(final String name) { if (name != null && -1 == name.indexOf('@')) { return true; } @@ -259,7 +258,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { /** * Checks if the words in a name are in the current binary dictionary. */ - private boolean isNameInDictionary(String name) { + private boolean isNameInDictionary(final String name) { int len = StringUtils.codePointCount(name); String prevWord = null; for (int i = 0; i < len; i++) { diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java index 3af3cab2c..731b9baf5 100644 --- a/java/src/com/android/inputmethod/latin/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/DebugSettings.java @@ -37,6 +37,8 @@ public final class DebugSettings extends PreferenceFragment private static final String DEBUG_MODE_KEY = "debug_mode"; public static final String FORCE_NON_DISTINCT_MULTITOUCH_KEY = "force_non_distinct_multitouch"; public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; + private static final String PREF_STATISTICS_LOGGING_KEY = "enable_logging"; + private static final boolean SHOW_STATISTICS_LOGGING = false; private boolean mServiceNeedsRestart = false; private CheckBoxPreference mDebugMode; @@ -55,6 +57,12 @@ public final class DebugSettings extends PreferenceFragment ResearchLogger.DEFAULT_USABILITY_STUDY_MODE)); checkbox.setSummary(R.string.settings_warning_researcher_mode); } + final Preference statisticsLoggingPref = findPreference(PREF_STATISTICS_LOGGING_KEY); + if (statisticsLoggingPref instanceof CheckBoxPreference) { + if (!SHOW_STATISTICS_LOGGING) { + getPreferenceScreen().removePreference(statisticsLoggingPref); + } + } mServiceNeedsRestart = false; mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY); diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 88d0c09dd..a218509f3 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -26,11 +26,6 @@ import java.util.ArrayList; * strokes. */ public abstract class Dictionary { - /** - * The weight to give to a word if it's length is the same as the number of typed characters. - */ - protected static final int FULL_WORD_SCORE_MULTIPLIER = 2; - public static final int NOT_A_PROBABILITY = -1; public static final String TYPE_USER_TYPED = "user_typed"; @@ -59,12 +54,12 @@ public abstract class Dictionary { // TODO: pass more context than just the previous word, to enable better suggestions (n-gram // and more) abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo); + final String prevWord, final ProximityInfo proximityInfo); // The default implementation of this method ignores sessionId. // Subclasses that want to use sessionId need to override this method. public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) { + final String prevWord, final ProximityInfo proximityInfo, final int sessionId) { return getSuggestions(composer, prevWord, proximityInfo); } @@ -73,9 +68,9 @@ public abstract class Dictionary { * @param word the word to search for. The search should be case-insensitive. * @return true if the word exists, false otherwise */ - abstract public boolean isValidWord(CharSequence word); + abstract public boolean isValidWord(final String word); - public int getFrequency(CharSequence word) { + public int getFrequency(final String word) { return NOT_A_PROBABILITY; } @@ -87,7 +82,7 @@ public abstract class Dictionary { * @param typedWord the word to compare with * @return true if they are the same, false otherwise. */ - protected boolean same(final char[] word, final int length, final CharSequence typedWord) { + protected boolean same(final char[] word, final int length, final String typedWord) { if (typedWord.length() != length) { return false; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index d3b120989..7f78ac8a2 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -56,7 +56,7 @@ public final class DictionaryCollection extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { + final String prevWord, final ProximityInfo proximityInfo) { final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries; if (dictionaries.isEmpty()) return null; // To avoid creating unnecessary objects, we get the list out of the first @@ -74,14 +74,14 @@ public final class DictionaryCollection extends Dictionary { } @Override - public boolean isValidWord(CharSequence word) { + public boolean isValidWord(final String word) { for (int i = mDictionaries.size() - 1; i >= 0; --i) if (mDictionaries.get(i).isValidWord(word)) return true; return false; } @Override - public int getFrequency(CharSequence word) { + public int getFrequency(final String word) { int maxFreq = -1; for (int i = mDictionaries.size() - 1; i >= 0; --i) { final int tempFreq = mDictionaries.get(i).getFrequency(word); diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index b93c17f11..159867ade 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -198,7 +198,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { + final String prevWord, final ProximityInfo proximityInfo) { asyncReloadDictionaryIfRequired(); if (mLocalDictionaryController.tryLock()) { try { @@ -213,12 +213,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public boolean isValidWord(final CharSequence word) { + public boolean isValidWord(final String word) { asyncReloadDictionaryIfRequired(); return isValidWordInner(word); } - protected boolean isValidWordInner(final CharSequence word) { + protected boolean isValidWordInner(final String word) { if (mLocalDictionaryController.tryLock()) { try { return isValidWordLocked(word); @@ -229,17 +229,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return false; } - protected boolean isValidWordLocked(final CharSequence word) { + protected boolean isValidWordLocked(final String word) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidWord(word); } - protected boolean isValidBigram(final CharSequence word1, final CharSequence word2) { + protected boolean isValidBigram(final String word1, final String word2) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidBigram(word1, word2); } - protected boolean isValidBigramInner(final CharSequence word1, final CharSequence word2) { + protected boolean isValidBigramInner(final String word1, final String word2) { if (mLocalDictionaryController.tryLock()) { try { return isValidBigramLocked(word1, word2); @@ -250,7 +250,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return false; } - protected boolean isValidBigramLocked(final CharSequence word1, final CharSequence word2) { + protected boolean isValidBigramLocked(final String word1, final String word2) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidBigram(word1, word2); } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 8cdc2a0af..fa0d5497b 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin; import android.content.Context; import android.text.TextUtils; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams; @@ -32,6 +31,10 @@ import java.util.LinkedList; * be searched for suggestions and valid words. */ public class ExpandableDictionary extends Dictionary { + /** + * The weight to give to a word if it's length is the same as the number of typed characters. + */ + private static final int FULL_WORD_SCORE_MULTIPLIER = 2; // Bigram frequency is a fixed point number with 1 meaning 1.2 and 255 meaning 1.8. protected static final int BIGRAM_MAX_FREQUENCY = 255; @@ -69,7 +72,7 @@ public class ExpandableDictionary extends Dictionary { mData = new Node[INCREMENT]; } - void add(Node n) { + void add(final Node n) { if (mLength + 1 > mData.length) { Node[] tempData = new Node[mLength + INCREMENT]; if (mLength > 0) { @@ -172,7 +175,7 @@ public class ExpandableDictionary extends Dictionary { } } - public void setRequiresReload(boolean reload) { + public void setRequiresReload(final boolean reload) { synchronized (mUpdatingLock) { mRequiresReload = reload; } @@ -202,8 +205,8 @@ public class ExpandableDictionary extends Dictionary { addWordRec(mRoots, word, 0, shortcutTarget, frequency, null); } - private void addWordRec(NodeArray children, final String word, final int depth, - final String shortcutTarget, final int frequency, Node parentNode) { + private void addWordRec(final NodeArray children, final String word, final int depth, + final String shortcutTarget, final int frequency, final Node parentNode) { final int wordLength = word.length(); if (wordLength <= depth) return; final char c = word.charAt(depth); @@ -248,7 +251,7 @@ public class ExpandableDictionary extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { + final String prevWord, final ProximityInfo proximityInfo) { if (reloadDictionaryIfRequired()) return null; if (composer.size() > 1) { if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) { @@ -267,8 +270,7 @@ public class ExpandableDictionary extends Dictionary { // This reloads the dictionary if required, and returns whether it's currently updating its // contents or not. - // @VisibleForTesting - boolean reloadDictionaryIfRequired() { + private boolean reloadDictionaryIfRequired() { synchronized (mUpdatingLock) { // If we need to update, start off a background task if (mRequiresReload) startDictionaryLoadingTaskLocked(); @@ -277,7 +279,7 @@ public class ExpandableDictionary extends Dictionary { } protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes, - final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { + final String prevWordForBigrams, final ProximityInfo proximityInfo) { final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); mInputLength = codes.size(); if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; @@ -305,7 +307,7 @@ public class ExpandableDictionary extends Dictionary { } @Override - public synchronized boolean isValidWord(CharSequence word) { + public synchronized boolean isValidWord(final String word) { synchronized (mUpdatingLock) { // If we need to update, start off a background task if (mRequiresReload) startDictionaryLoadingTaskLocked(); @@ -320,7 +322,7 @@ public class ExpandableDictionary extends Dictionary { return (node == null) ? false : !node.mShortcutOnly; } - protected boolean removeBigram(String word1, String word2) { + protected boolean removeBigram(final String word1, final String word2) { // Refer to addOrSetBigram() about word1.toLowerCase() final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null); final Node secondWord = searchWord(mRoots, word2, 0, null); @@ -345,13 +347,13 @@ public class ExpandableDictionary extends Dictionary { /** * Returns the word's frequency or -1 if not found */ - protected int getWordFrequency(CharSequence word) { + protected int getWordFrequency(final String word) { // Case-sensitive search final Node node = searchNode(mRoots, word, 0, word.length()); return (node == null) ? -1 : node.mFrequency; } - protected NextWord getBigramWord(String word1, String word2) { + protected NextWord getBigramWord(final String word1, final String word2) { // Refer to addOrSetBigram() about word1.toLowerCase() final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null); final Node secondWord = searchWord(mRoots, word2, 0, null); @@ -368,7 +370,8 @@ public class ExpandableDictionary extends Dictionary { return null; } - private static int computeSkippedWordFinalFreq(int freq, int snr, int inputLength) { + private static int computeSkippedWordFinalFreq(final int freq, final int snr, + final int inputLength) { // The computation itself makes sense for >= 2, but the == 2 case returns 0 // anyway so we may as well test against 3 instead and return the constant if (inputLength >= 3) { @@ -431,9 +434,9 @@ public class ExpandableDictionary extends Dictionary { * @param suggestions the list in which to add suggestions */ // TODO: Share this routine with the native code for BinaryDictionary - protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word, - final int depth, final boolean completion, int snr, int inputIndex, int skipPos, - final ArrayList<SuggestedWordInfo> suggestions) { + protected void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word, + final int depth, final boolean completion, final int snr, final int inputIndex, + final int skipPos, final ArrayList<SuggestedWordInfo> suggestions) { final int count = roots.mLength; final int codeSize = mInputLength; // Optimization: Prune out words that are too long compared to how much was typed. @@ -472,8 +475,8 @@ public class ExpandableDictionary extends Dictionary { getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex, skipPos, suggestions); } - } else if ((c == Keyboard.CODE_SINGLE_QUOTE - && currentChars[0] != Keyboard.CODE_SINGLE_QUOTE) || depth == skipPos) { + } else if ((c == Constants.CODE_SINGLE_QUOTE + && currentChars[0] != Constants.CODE_SINGLE_QUOTE) || depth == skipPos) { // Skip the ' and continue deeper word[depth] = c; if (children != null) { @@ -524,11 +527,13 @@ public class ExpandableDictionary extends Dictionary { } } - public int setBigramAndGetFrequency(String word1, String word2, int frequency) { + public int setBigramAndGetFrequency(final String word1, final String word2, + final int frequency) { return setBigramAndGetFrequency(word1, word2, frequency, null /* unused */); } - public int setBigramAndGetFrequency(String word1, String word2, ForgettingCurveParams fcp) { + public int setBigramAndGetFrequency(final String word1, final String word2, + final ForgettingCurveParams fcp) { return setBigramAndGetFrequency(word1, word2, 0 /* unused */, fcp); } @@ -540,8 +545,8 @@ public class ExpandableDictionary extends Dictionary { * @param fcp an instance of ForgettingCurveParams to use for decay policy * @return returns the final bigram frequency */ - private int setBigramAndGetFrequency( - String word1, String word2, int frequency, ForgettingCurveParams fcp) { + private int setBigramAndGetFrequency(final String word1, final String word2, + final int frequency, final ForgettingCurveParams fcp) { // We don't want results to be different according to case of the looked up left hand side // word. We do want however to return the correct case for the right hand side. // So we want to squash the case of the left hand side, and preserve that of the right @@ -572,7 +577,8 @@ public class ExpandableDictionary extends Dictionary { * Searches for the word and add the word if it does not exist. * @return Returns the terminal node of the word we are searching for. */ - private Node searchWord(NodeArray children, String word, int depth, Node parentNode) { + private Node searchWord(final NodeArray children, final String word, final int depth, + final Node parentNode) { final int wordLength = word.length(); final char c = word.charAt(depth); // Does children have the current character? @@ -602,36 +608,17 @@ public class ExpandableDictionary extends Dictionary { return searchWord(childNode.mChildren, word, depth + 1, childNode); } - private void runBigramReverseLookUp(final CharSequence previousWord, + private void runBigramReverseLookUp(final String previousWord, final ArrayList<SuggestedWordInfo> suggestions) { // Search for the lowercase version of the word only, because that's where bigrams // store their sons. - Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0, + final Node prevWord = searchNode(mRoots, previousWord.toLowerCase(), 0, previousWord.length()); if (prevWord != null && prevWord.mNGrams != null) { reverseLookUp(prevWord.mNGrams, suggestions); } } - /** - * Used for testing purposes and in the spell checker - * This function will wait for loading from database to be done - */ - void waitForDictionaryLoading() { - while (mUpdatingDictionary) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // - } - } - } - - protected final void blockingReloadDictionaryIfRequired() { - reloadDictionaryIfRequired(); - waitForDictionaryLoading(); - } - // Local to reverseLookUp, but do not allocate each time. private final char[] mLookedUpString = new char[BinaryDictionary.MAX_WORD_LENGTH]; @@ -641,7 +628,7 @@ public class ExpandableDictionary extends Dictionary { * @param terminalNodes list of terminal nodes we want to add * @param suggestions the suggestion collection to add the word to */ - private void reverseLookUp(LinkedList<NextWord> terminalNodes, + private void reverseLookUp(final LinkedList<NextWord> terminalNodes, final ArrayList<SuggestedWordInfo> suggestions) { Node node; int freq; @@ -714,7 +701,7 @@ public class ExpandableDictionary extends Dictionary { } } - private static char toLowerCase(char c) { + private static char toLowerCase(final char c) { char baseChar = c; if (c < BASE_CHARS.length) { baseChar = BASE_CHARS[c]; diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java deleted file mode 100644 index 2674e4575..000000000 --- a/java/src/com/android/inputmethod/latin/ImfUtils.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; - -import android.content.Context; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; - -import java.util.Collections; -import java.util.List; - -/** - * Utility class for Input Method Framework - */ -public final class ImfUtils { - private ImfUtils() { - // This utility class is not publicly instantiable. - } - - private static InputMethodManager sInputMethodManager; - - public static InputMethodManager getInputMethodManager(Context context) { - if (sInputMethodManager == null) { - sInputMethodManager = (InputMethodManager)context.getSystemService( - Context.INPUT_METHOD_SERVICE); - } - return sInputMethodManager; - } - - private static InputMethodInfo sInputMethodInfoOfThisIme; - - public static InputMethodInfo getInputMethodInfoOfThisIme(Context context) { - if (sInputMethodInfoOfThisIme == null) { - final InputMethodManager imm = getInputMethodManager(context); - final String packageName = context.getPackageName(); - for (final InputMethodInfo imi : imm.getInputMethodList()) { - if (imi.getPackageName().equals(packageName)) - return imi; - } - throw new RuntimeException("Can not find input method id for " + packageName); - } - return sInputMethodInfoOfThisIme; - } - - public static String getInputMethodIdOfThisIme(Context context) { - return getInputMethodInfoOfThisIme(context).getId(); - } - - public static boolean checkIfSubtypeBelongsToThisImeAndEnabled(Context context, - InputMethodSubtype ims) { - final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context); - final InputMethodManager imm = getInputMethodManager(context); - // TODO: Cache all subtypes of this IME for optimization - final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(myImi, true); - for (final InputMethodSubtype subtype : subtypes) { - if (subtype.equals(ims)) { - return true; - } - } - return false; - } - - public static boolean checkIfSubtypeBelongsToThisIme(Context context, - InputMethodSubtype ims) { - final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context); - final int count = myImi.getSubtypeCount(); - for (int i = 0; i < count; i++) { - final InputMethodSubtype subtype = myImi.getSubtypeAt(i); - if (subtype.equals(ims)) { - return true; - } - } - return false; - } - - public static InputMethodSubtype getCurrentInputMethodSubtype(Context context, - InputMethodSubtype defaultSubtype) { - final InputMethodManager imm = getInputMethodManager(context); - final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); - return (currentSubtype != null) ? currentSubtype : defaultSubtype; - } - - public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context, - final boolean shouldIncludeAuxiliarySubtypes) { - final InputMethodManager imm = getInputMethodManager(context); - final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList(); - return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, enabledImis); - } - - public static boolean hasMultipleEnabledSubtypesInThisIme(Context context, - final boolean shouldIncludeAuxiliarySubtypes) { - final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context); - final List<InputMethodInfo> imiList = Collections.singletonList(myImi); - return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, imiList); - } - - private static boolean hasMultipleEnabledSubtypes(Context context, - final boolean shouldIncludeAuxiliarySubtypes, List<InputMethodInfo> imiList) { - final InputMethodManager imm = getInputMethodManager(context); - - // Number of the filtered IMEs - int filteredImisCount = 0; - - for (InputMethodInfo imi : imiList) { - // We can return true immediately after we find two or more filtered IMEs. - if (filteredImisCount > 1) return true; - final List<InputMethodSubtype> subtypes = - imm.getEnabledInputMethodSubtypeList(imi, true); - // IMEs that have no subtypes should be counted. - if (subtypes.isEmpty()) { - ++filteredImisCount; - continue; - } - - int auxCount = 0; - for (InputMethodSubtype subtype : subtypes) { - 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; - } - } - - if (filteredImisCount > 1) { - return true; - } - final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(null, true); - int keyboardCount = 0; - // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's - // both explicitly and implicitly enabled input method subtype. - // (The current IME should be LatinIME.) - for (InputMethodSubtype subtype : subtypes) { - if (KEYBOARD_MODE.equals(subtype.getMode())) { - ++keyboardCount; - } - } - return keyboardCount > 1; - } - - public static InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet( - Context context, String localeString, String keyboardLayoutSetName) { - final InputMethodInfo imi = getInputMethodInfoOfThisIme(context); - final int count = imi.getSubtypeCount(); - for (int i = 0; i < count; i++) { - final InputMethodSubtype subtype = imi.getSubtypeAt(i); - final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype); - if (localeString.equals(subtype.getLocale()) - && keyboardLayoutSetName.equals(layoutName)) { - return subtype; - } - } - return null; - } - - public static void setAdditionalInputMethodSubtypes(Context context, - InputMethodSubtype[] subtypes) { - final InputMethodManager imm = getInputMethodManager(context); - final String imiId = getInputMethodIdOfThisIme(context); - imm.setAdditionalInputMethodSubtypes(imiId, subtypes); - } -} diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 2f7608a03..6c1e0301a 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -112,36 +112,56 @@ public final class InputAttributes { if (inputClass == InputType.TYPE_CLASS_DATETIME) Log.i(TAG, " TYPE_CLASS_DATETIME"); Log.i(TAG, "Variation:"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS)) - Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_ADDRESS"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT)) - Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_SUBJECT"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_FILTER)) - Log.i(TAG, " TYPE_TEXT_VARIATION_FILTER"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE)) - Log.i(TAG, " TYPE_TEXT_VARIATION_LONG_MESSAGE"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_NORMAL)) - Log.i(TAG, " TYPE_TEXT_VARIATION_NORMAL"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_PASSWORD)) - Log.i(TAG, " TYPE_TEXT_VARIATION_PASSWORD"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_PERSON_NAME)) - Log.i(TAG, " TYPE_TEXT_VARIATION_PERSON_NAME"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_PHONETIC)) - Log.i(TAG, " TYPE_TEXT_VARIATION_PHONETIC"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS)) - Log.i(TAG, " TYPE_TEXT_VARIATION_POSTAL_ADDRESS"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)) - Log.i(TAG, " TYPE_TEXT_VARIATION_SHORT_MESSAGE"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_URI)) - Log.i(TAG, " TYPE_TEXT_VARIATION_URI"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) - Log.i(TAG, " TYPE_TEXT_VARIATION_VISIBLE_PASSWORD"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT)) - Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS)) - Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"); - if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)) - Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_PASSWORD"); + switch (InputType.TYPE_MASK_VARIATION & inputType) { + case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS: + Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_ADDRESS"); + break; + case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT: + Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_SUBJECT"); + break; + case InputType.TYPE_TEXT_VARIATION_FILTER: + Log.i(TAG, " TYPE_TEXT_VARIATION_FILTER"); + break; + case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE: + Log.i(TAG, " TYPE_TEXT_VARIATION_LONG_MESSAGE"); + break; + case InputType.TYPE_TEXT_VARIATION_NORMAL: + Log.i(TAG, " TYPE_TEXT_VARIATION_NORMAL"); + break; + case InputType.TYPE_TEXT_VARIATION_PASSWORD: + Log.i(TAG, " TYPE_TEXT_VARIATION_PASSWORD"); + break; + case InputType.TYPE_TEXT_VARIATION_PERSON_NAME: + Log.i(TAG, " TYPE_TEXT_VARIATION_PERSON_NAME"); + break; + case InputType.TYPE_TEXT_VARIATION_PHONETIC: + Log.i(TAG, " TYPE_TEXT_VARIATION_PHONETIC"); + break; + case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS: + Log.i(TAG, " TYPE_TEXT_VARIATION_POSTAL_ADDRESS"); + break; + case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE: + Log.i(TAG, " TYPE_TEXT_VARIATION_SHORT_MESSAGE"); + break; + case InputType.TYPE_TEXT_VARIATION_URI: + Log.i(TAG, " TYPE_TEXT_VARIATION_URI"); + break; + case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: + Log.i(TAG, " TYPE_TEXT_VARIATION_VISIBLE_PASSWORD"); + break; + case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: + Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"); + break; + case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS: + Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"); + break; + case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD: + Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_PASSWORD"); + break; + default: + Log.i(TAG, " Unknown variation"); + break; + } Log.i(TAG, "Flags:"); if (0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)) Log.i(TAG, " TYPE_TEXT_FLAG_NO_SUGGESTIONS"); diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index 6b48aabb3..7dffd96dd 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.annotations.UsedForTesting; + // TODO: This class is not thread-safe. public final class InputPointers { private final int mDefaultCapacity; @@ -39,7 +41,8 @@ public final class InputPointers { mTimes.add(index, time); } - public void addPointer(int x, int y, int pointerId, int time) { + @UsedForTesting + void addPointer(int x, int y, int pointerId, int time) { mXCoordinates.add(x); mYCoordinates.add(y); mPointerIds.add(pointerId); @@ -66,7 +69,8 @@ public final class InputPointers { * @param startPos the starting index of the pointers in {@code src}. * @param length the number of pointers to be appended. */ - public void append(InputPointers src, int startPos, int length) { + @UsedForTesting + void append(InputPointers src, int startPos, int length) { if (length == 0) { return; } diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 94cdc9b85..44ef01204 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -44,7 +44,7 @@ public final class LastComposedWord { public final String mTypedWord; public final String mCommittedWord; public final String mSeparatorString; - public final CharSequence mPrevWord; + public final String mPrevWord; public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH); private boolean mActive; @@ -56,7 +56,7 @@ public final class LastComposedWord { // immutable. Do not fiddle with their contents after you passed them to this constructor. public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers, final String typedWord, final String committedWord, - final String separatorString, final CharSequence prevWord) { + final String separatorString, final String prevWord) { mPrimaryKeyCodes = primaryKeyCodes; if (inputPointers != null) { mInputPointers.copy(inputPointers); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ddfc27310..f6c5f8277 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -60,8 +60,8 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.CompatUtils; -import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.keyboard.KeyDetector; @@ -132,14 +132,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private View mKeyPreviewBackingView; private View mSuggestionsContainer; private SuggestionStripView mSuggestionStripView; - /* package for tests */ Suggest mSuggest; + @UsedForTesting Suggest mSuggest; private CompletionInfo[] mApplicationSpecifiedCompletions; private ApplicationInfo mTargetApplicationInfo; - private InputMethodManagerCompatWrapper mImm; + private RichInputMethodManager mRichImm; private Resources mResources; private SharedPreferences mPrefs; - /* package for tests */ final KeyboardSwitcher mKeyboardSwitcher; + @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); @@ -163,17 +163,17 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private int mDeleteCount; private long mLastKeyTime; - private AudioAndHapticFeedbackManager mFeedbackManager; - // Member variables for remembering the current device orientation. private int mDisplayOrientation; // Object for reacting to adding/removing a dictionary pack. + // TODO: The experimental version is not supported by the Dictionary Pack Service yet. private BroadcastReceiver mDictionaryPackInstallReceiver = - new DictionaryPackInstallBroadcastReceiver(this); + ProductionFlag.IS_EXPERIMENTAL + ? null : new DictionaryPackInstallBroadcastReceiver(this); // Keeps track of most recently inserted text (multi-character key) for reverting - private CharSequence mEnteredText; + private String mEnteredText; private boolean mIsAutoCorrectionIndicatorOn; @@ -373,9 +373,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mCurrentSubtypeUsed = true; } - public void switchSubtype(final IBinder token, final InputMethodManagerCompatWrapper imm, - final Context context) { - final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); + public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) { + final InputMethodSubtype currentSubtype = richImm.getInputMethodManager() + .getCurrentInputMethodSubtype(); final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype; final boolean currentSubtypeUsed = mCurrentSubtypeUsed; if (currentSubtypeUsed) { @@ -383,13 +383,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mCurrentSubtypeUsed = false; } if (currentSubtypeUsed - && ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(context, lastActiveSubtype) + && richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype) && !currentSubtype.equals(lastActiveSubtype)) { - final String id = ImfUtils.getInputMethodIdOfThisIme(context); - imm.setInputMethodAndSubtype(token, id, lastActiveSubtype); + richImm.setInputMethodAndSubtype(token, lastActiveSubtype); return; } - imm.switchToNextInputMethod(token, true /* onlyCurrentIme */); + richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */); } } @@ -410,14 +409,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.getInstance().init(this, prefs); } - InputMethodManagerCompatWrapper.init(this); + RichInputMethodManager.init(this); SubtypeSwitcher.init(this); KeyboardSwitcher.init(this, prefs); AccessibilityUtils.init(this); super.onCreate(); - mImm = InputMethodManagerCompatWrapper.getInstance(); + mRichImm = RichInputMethodManager.getInstance(); mHandler.onCreate(); DEBUG = LatinImeLogger.sDBG; @@ -426,7 +425,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction loadSettings(); - ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes()); + mRichImm.setAdditionalInputMethodSubtypes(mCurrentSettings.getAdditionalSubtypes()); initSuggest(); @@ -439,20 +438,23 @@ public final class LatinIME extends InputMethodService implements KeyboardAction filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); registerReceiver(mReceiver, filter); - final IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addDataScheme(SCHEME_PACKAGE); - registerReceiver(mDictionaryPackInstallReceiver, packageFilter); + // TODO: The experimental version is not supported by the Dictionary Pack Service yet. + if (!ProductionFlag.IS_EXPERIMENTAL) { + final IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilter.addDataScheme(SCHEME_PACKAGE); + registerReceiver(mDictionaryPackInstallReceiver, packageFilter); - final IntentFilter newDictFilter = new IntentFilter(); - newDictFilter.addAction( - DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION); - registerReceiver(mDictionaryPackInstallReceiver, newDictFilter); + final IntentFilter newDictFilter = new IntentFilter(); + newDictFilter.addAction( + DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION); + registerReceiver(mDictionaryPackInstallReceiver, newDictFilter); + } } // Has to be package-visible for unit tests - /* package for test */ + @UsedForTesting void loadSettings() { // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. @@ -466,7 +468,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } }; mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale()); - mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings); resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); } @@ -569,7 +570,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mSuggest = null; } unregisterReceiver(mReceiver); - unregisterReceiver(mDictionaryPackInstallReceiver); + // TODO: The experimental version is not supported by the Dictionary Pack Service yet. + if (!ProductionFlag.IS_EXPERIMENTAL) { + unregisterReceiver(mDictionaryPackInstallReceiver); + } LatinImeLogger.commit(); LatinImeLogger.onDestroy(); super.onDestroy(); @@ -578,7 +582,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction @Override public void onConfigurationChanged(final Configuration conf) { // System locale has been changed. Needs to reload keyboard. - if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) { + if (mSubtypeSwitcher.onConfigurationChanged(conf)) { loadKeyboard(); } // If orientation changed while predicting, commit the change @@ -718,8 +722,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); if (!currentSubtypeEnabled) { // Current subtype is disabled. Needs to update subtype and keyboard. - final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype( - this, mSubtypeSwitcher.getNoLanguageSubtype()); + final InputMethodSubtype newSubtype = mRichImm.getCurrentInputMethodSubtype( + mSubtypeSwitcher.getNoLanguageSubtype()); mSubtypeSwitcher.updateSubtype(newSubtype); loadKeyboard(); } @@ -1129,7 +1133,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void commitTyped(final String separatorString) { if (!mWordComposer.isComposingWord()) return; - final CharSequence typedWord = mWordComposer.getTypedWord(); + final String typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString); @@ -1166,7 +1170,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. if (lastTwo != null && lastTwo.length() == 2 - && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { + && lastTwo.charAt(0) == Constants.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { @@ -1182,8 +1186,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 && canBeFollowedByPeriod(lastThree.charAt(0)) - && lastThree.charAt(1) == Keyboard.CODE_SPACE - && lastThree.charAt(2) == Keyboard.CODE_SPACE) { + && lastThree.charAt(1) == Constants.CODE_SPACE + && lastThree.charAt(2) == Constants.CODE_SPACE) { mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); @@ -1197,12 +1201,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // 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; + || codePoint == Constants.CODE_SINGLE_QUOTE + || codePoint == Constants.CODE_DOUBLE_QUOTE + || codePoint == Constants.CODE_CLOSING_PARENTHESIS + || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET + || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET + || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET; } // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is @@ -1230,9 +1234,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (isShowingOptionDialog()) return false; switch (requestCode) { case CODE_SHOW_INPUT_METHOD_PICKER: - if (ImfUtils.hasMultipleEnabledIMEsOrSubtypes( - this, true /* include aux subtypes */)) { - mImm.showInputMethodPicker(); + if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { + mRichImm.getInputMethodManager().showInputMethodPicker(); return true; } return false; @@ -1256,10 +1259,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void handleLanguageSwitchKey() { final IBinder token = getWindow().getWindow().getAttributes().token; if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) { - mImm.switchToNextInputMethod(token, false /* onlyCurrentIme */); + mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */); return; } - mSubtypeState.switchSubtype(token, mImm, this); + mSubtypeState.switchSubtype(token, mRichImm); } private void sendDownUpKeyEventForBackwardCompatibility(final int code) { @@ -1285,7 +1288,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because // we want to be able to compile against the Ice Cream Sandwich SDK. - if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null + if (Constants.CODE_ENTER == code && mTargetApplicationInfo != null && mTargetApplicationInfo.targetSdkVersion < 16) { // Backward compatibility mode. Before Jelly bean, the keyboard would simulate // a hardware keyboard event on pressing enter or delete. This is bad for many @@ -1302,7 +1305,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction @Override public void onCodeInput(final int primaryCode, final int x, final int y) { final long when = SystemClock.uptimeMillis(); - if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) { + if (primaryCode != Constants.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) { mDeleteCount = 0; } mLastKeyTime = when; @@ -1317,42 +1320,42 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (!mWordComposer.isComposingWord()) mIsAutoCorrectionIndicatorOn = false; // TODO: Consolidate the double space timer, mLastKeyTime, and the space state. - if (primaryCode != Keyboard.CODE_SPACE) { + if (primaryCode != Constants.CODE_SPACE) { mHandler.cancelDoubleSpacesTimer(); } boolean didAutoCorrect = false; switch (primaryCode) { - case Keyboard.CODE_DELETE: + case Constants.CODE_DELETE: mSpaceState = SPACE_STATE_NONE; handleBackspace(spaceState); mDeleteCount++; mExpectingUpdateSelection = true; LatinImeLogger.logOnDelete(x, y); break; - case Keyboard.CODE_SHIFT: - case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: + case Constants.CODE_SHIFT: + case Constants.CODE_SWITCH_ALPHA_SYMBOL: // Shift and symbol key is handled in onPressKey() and onReleaseKey(). break; - case Keyboard.CODE_SETTINGS: + case Constants.CODE_SETTINGS: onSettingsKeyPressed(); break; - case Keyboard.CODE_SHORTCUT: + case Constants.CODE_SHORTCUT: mSubtypeSwitcher.switchToShortcutIME(this); break; - case Keyboard.CODE_ACTION_ENTER: + case Constants.CODE_ACTION_ENTER: performEditorAction(getActionId(switcher.getKeyboard())); break; - case Keyboard.CODE_ACTION_NEXT: + case Constants.CODE_ACTION_NEXT: performEditorAction(EditorInfo.IME_ACTION_NEXT); break; - case Keyboard.CODE_ACTION_PREVIOUS: + case Constants.CODE_ACTION_PREVIOUS: performEditorAction(EditorInfo.IME_ACTION_PREVIOUS); break; - case Keyboard.CODE_LANGUAGE_SWITCH: + case Constants.CODE_LANGUAGE_SWITCH: handleLanguageSwitchKey(); break; - case Keyboard.CODE_RESEARCH: + case Constants.CODE_RESEARCH: if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.getInstance().onResearchKeySelected(this); } @@ -1387,10 +1390,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } switcher.onCodeInput(primaryCode); // Reset after any single keystroke, except shift and symbol-shift - if (!didAutoCorrect && primaryCode != Keyboard.CODE_SHIFT - && primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL) + if (!didAutoCorrect && primaryCode != Constants.CODE_SHIFT + && primaryCode != Constants.CODE_SWITCH_ALPHA_SYMBOL) mLastComposedWord.deactivate(); - if (Keyboard.CODE_DELETE != primaryCode) { + if (Constants.CODE_DELETE != primaryCode) { mEnteredText = null; } mConnection.endBatchEdit(); @@ -1401,7 +1404,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Called from PointerTracker through the KeyboardActionListener interface @Override - public void onTextInput(final CharSequence rawText) { + public void onTextInput(final String rawText) { mConnection.beginBatchEdit(); if (mWordComposer.isComposingWord()) { commitCurrentAutoCorrection(rawText.toString()); @@ -1409,7 +1412,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction resetComposingState(true /* alsoResetLastComposedWord */); } mHandler.postUpdateSuggestionStrip(); - final CharSequence text = specificTldProcessingOnTextInput(rawText); + final String text = specificTldProcessingOnTextInput(rawText); if (SPACE_STATE_PHANTOM == mSpaceState) { promotePhantomSpace(); } @@ -1418,7 +1421,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Space state must be updated before calling updateShiftState mSpaceState = SPACE_STATE_NONE; mKeyboardSwitcher.updateShiftState(); - mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); + mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT); mEnteredText = text; } @@ -1547,8 +1550,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { - final String batchInputText = (suggestedWords.size() > 0) - ? suggestedWords.getWord(0) : null; + final String batchInputText = suggestedWords.isEmpty() + ? null : suggestedWords.getWord(0); final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); mainKeyboardView.showGestureFloatingPreviewText(batchInputText); showSuggestionStrip(suggestedWords, null); @@ -1566,8 +1569,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction public void onEndBatchInput(final InputPointers batchPointers) { final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput( batchPointers, this); - final String batchInputText = (suggestedWords.size() > 0) - ? suggestedWords.getWord(0) : null; + final String batchInputText = suggestedWords.isEmpty() + ? null : suggestedWords.getWord(0); if (TextUtils.isEmpty(batchInputText)) { return; } @@ -1584,8 +1587,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mKeyboardSwitcher.updateShiftState(); } - private CharSequence specificTldProcessingOnTextInput(final CharSequence text) { - if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD + private String specificTldProcessingOnTextInput(final String text) { + if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD || !Character.isLetter(text.charAt(1))) { // Not a tld: do nothing. return text; @@ -1596,8 +1599,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 - && lastOne.charAt(0) == Keyboard.CODE_PERIOD) { - return text.subSequence(1, text.length()); + && lastOne.charAt(0) == Constants.CODE_PERIOD) { + return text.substring(1); } else { return text; } @@ -1619,8 +1622,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (mWordComposer.isComposingWord()) { final int length = mWordComposer.size(); if (length > 0) { - // Immediately after a batch input. - if (SPACE_STATE_PHANTOM == spaceState) { + if (mWordComposer.isBatchMode()) { mWordComposer.reset(); } else { mWordComposer.deleteLast(); @@ -1701,7 +1703,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private boolean maybeStripSpace(final int code, final int spaceState, final boolean isFromSuggestionStrip) { - if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { + if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { mConnection.removeTrailingSpace(); return false; } else if ((SPACE_STATE_WEAK == spaceState @@ -1744,7 +1746,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // the character is a single quote. The idea here is, single quote is not a // separator and it should be treated as a normal character, except in the first // position where it should not start composing a word. - isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != primaryCode); + isComposingWord = (Constants.CODE_SINGLE_QUOTE != primaryCode); // Here we don't need to reset the last composed word. It will be reset // when we commit this one, if we ever do; if on the other hand we backspace // it entirely and resume suggestions on the previous word, we'd like to still @@ -1753,15 +1755,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } if (isComposingWord) { final int keyX, keyY; - if (KeyboardActionListener.Adapter.isInvalidCoordinate(x) - || KeyboardActionListener.Adapter.isInvalidCoordinate(y)) { - keyX = x; - keyY = y; - } else { + if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) { final KeyDetector keyDetector = mKeyboardSwitcher.getMainKeyboardView().getKeyDetector(); keyX = keyDetector.getTouchX(x); keyY = keyDetector.getTouchY(y); + } else { + keyX = x; + keyY = y; } mWordComposer.add(primaryCode, keyX, keyY); // If it's the first letter, make note of auto-caps state @@ -1812,7 +1813,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } sendKeyCodePoint(primaryCode); - if (Keyboard.CODE_SPACE == primaryCode) { + if (Constants.CODE_SPACE == primaryCode) { if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { if (maybeDoubleSpace()) { mSpaceState = SPACE_STATE_DOUBLE; @@ -1857,7 +1858,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction return didAutoCorrect; } - private CharSequence getTextWithUnderline(final CharSequence text) { + private CharSequence getTextWithUnderline(final String text) { return mIsAutoCorrectionIndicatorOn ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text) : text; @@ -1874,7 +1875,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // TODO: make this private // Outside LatinIME, only used by the test suite. - /* package for tests */ + @UsedForTesting boolean isShowingPunctuationList() { if (mSuggestionStripView == null) return false; return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions(); @@ -1944,7 +1945,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private SuggestedWords getSuggestedWords(final int sessionId) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); - if (keyboard == null) { + if (keyboard == null || mSuggest == null) { return SuggestedWords.EMPTY; } final String typedWord = mWordComposer.getTypedWord(); @@ -1952,7 +1953,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we // should just skip whitespace if any, so 1. // TODO: this is slow (2-way IPC) - we should probably cache this instead. - final CharSequence prevWord = + final String prevWord = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, mWordComposer.isComposingWord() ? 2 : 1); final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, @@ -1961,7 +1962,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction return maybeRetrieveOlderSuggestions(typedWord, suggestedWords); } - private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord, + private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord, final SuggestedWords suggestedWords) { // TODO: consolidate this into getSuggestedWords // We update the suggestion strip only when we have some suggestions to show, i.e. when @@ -1991,21 +1992,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } } - private void showSuggestionStrip(final SuggestedWords suggestedWords, - final CharSequence typedWord) { - if (null == suggestedWords || suggestedWords.size() <= 0) { + private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) { + if (suggestedWords.isEmpty()) { clearSuggestionStrip(); return; } - final CharSequence autoCorrection; - if (suggestedWords.size() > 0) { - if (suggestedWords.mWillAutoCorrect) { - autoCorrection = suggestedWords.getWord(1); - } else { - autoCorrection = typedWord; - } + final String autoCorrection; + if (suggestedWords.mWillAutoCorrect) { + autoCorrection = suggestedWords.getWord(1); } else { - autoCorrection = null; + autoCorrection = typedWord; } mWordComposer.setAutoCorrection(autoCorrection); final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); @@ -2019,9 +2015,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (mHandler.hasPendingUpdateSuggestions()) { updateSuggestionStrip(); } - final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); + final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); final String typedWord = mWordComposer.getTypedWord(); - final CharSequence autoCorrection = (typedAutoCorrection != null) + final String autoCorrection = (typedAutoCorrection != null) ? typedAutoCorrection : typedWord; if (autoCorrection != null) { if (TextUtils.isEmpty(typedWord)) { @@ -2052,7 +2048,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} // interface @Override - public void pickSuggestionManually(final int index, final CharSequence suggestion) { + public void pickSuggestionManually(final int index, final String suggestion) { final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput if (suggestion.length() == 1 && isShowingPunctuationList()) { @@ -2122,7 +2118,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true); if (ProductionFlag.IS_INTERNAL) { - Stats.onSeparator((char)Keyboard.CODE_SPACE, + Stats.onSeparator((char)Constants.CODE_SPACE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { @@ -2137,13 +2133,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction /** * Commits the chosen word to the text field and saves it for later retrieval. */ - private void commitChosenWord(final CharSequence chosenWord, final int commitType, + private void commitChosenWord(final String chosenWord, final int commitType, final String separatorString) { final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); // Add the word to the user history dictionary - final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); + final String prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually // what user typed. Note: currently this is done much later in // LastComposedWord#didCommitTypedWord by string equality of the remembered @@ -2162,7 +2158,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction setSuggestionStripShown(isSuggestionsStripVisible()); } - private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) { + private String addToUserHistoryDictionary(final String suggestion) { if (TextUtils.isEmpty(suggestion)) return null; if (mSuggest == null) return null; @@ -2177,19 +2173,18 @@ public final class LatinIME extends InputMethodService implements KeyboardAction = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2); final String secondWord; if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) { - secondWord = suggestion.toString().toLowerCase( - mSubtypeSwitcher.getCurrentSubtypeLocale()); + secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); } else { - secondWord = suggestion.toString(); + secondWord = suggestion; } // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid". // We don't add words with 0-frequency (assuming they would be profanity etc.). final int maxFreq = AutoCorrection.getMaxFrequency( mSuggest.getUnigramDictionaries(), suggestion); if (maxFreq == 0) return null; - userHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(), - secondWord, maxFreq > 0); - return prevWord; + final String prevWordString = (null == prevWord) ? null : prevWord.toString(); + userHistoryDictionary.addToUserHistory(prevWordString, secondWord, maxFreq > 0); + return prevWordString; } return null; } @@ -2214,9 +2209,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } private void revertCommit() { - final CharSequence previousWord = mLastComposedWord.mPrevWord; + final String previousWord = mLastComposedWord.mPrevWord; final String originallyTypedWord = mLastComposedWord.mTypedWord; - final CharSequence committedWord = mLastComposedWord.mCommittedWord; + final String committedWord = mLastComposedWord.mCommittedWord; final int cancelLength = committedWord.length(); final int separatorLength = LastComposedWord.getSeparatorLength( mLastComposedWord.mSeparatorString); @@ -2226,9 +2221,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (mWordComposer.isComposingWord()) { throw new RuntimeException("revertCommit, but we are composing a word"); } - final String wordBeforeCursor = + final CharSequence wordBeforeCursor = mConnection.getTextBeforeCursor(deleteLength, 0) - .subSequence(0, cancelLength).toString(); + .subSequence(0, cancelLength); if (!TextUtils.equals(committedWord, wordBeforeCursor)) { throw new RuntimeException("revertCommit check failed: we thought we were " + "reverting \"" + committedWord @@ -2257,7 +2252,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // This essentially inserts a space, and that's it. public void promotePhantomSpace() { - sendKeyCodePoint(Keyboard.CODE_SPACE); + sendKeyCodePoint(Constants.CODE_SPACE); } // Used by the RingCharBuffer @@ -2267,7 +2262,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // TODO: Make this private // Outside LatinIME, only used by the {@link InputTestsBase} test suite. - /* package for test */ + @UsedForTesting void loadKeyboard() { // When the device locale is changed in SetupWizard etc., this method may get called via // onConfigurationChanged before SoftInputWindow is shown. @@ -2283,13 +2278,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mHandler.postUpdateSuggestionStrip(); } - // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to - // {@link KeyboardSwitcher}. Called from KeyboardSwitcher - public void hapticAndAudioFeedback(final int primaryCode) { - mFeedbackManager.hapticAndAudioFeedback( - primaryCode, mKeyboardSwitcher.getMainKeyboardView()); - } - // Callback called by PointerTracker through the KeyboardActionListener. This is called when a // key is depressed; release matching call is onReleaseKey below. @Override @@ -2306,16 +2294,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // If accessibility is on, ensure the user receives keyboard state updates. if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { switch (primaryCode) { - case Keyboard.CODE_SHIFT: + case Constants.CODE_SHIFT: AccessibleKeyboardViewProxy.getInstance().notifyShiftState(); break; - case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: + case Constants.CODE_SWITCH_ALPHA_SYMBOL: AccessibleKeyboardViewProxy.getInstance().notifySymbolsState(); break; } } - if (Keyboard.CODE_DELETE == primaryCode) { + if (Constants.CODE_DELETE == primaryCode) { // This is a stopgap solution to avoid leaving a high surrogate alone in a text view. // In the future, we need to deprecate deteleSurroundingText() and have a surrogate // pair-friendly way of deleting characters in InputConnection. @@ -2335,7 +2323,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { mSubtypeSwitcher.onNetworkStateChanged(intent); } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { - mFeedbackManager.onRingerModeChanged(); + mKeyboardSwitcher.onRingerModeChanged(); } } }; @@ -2372,7 +2360,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction getString(R.string.language_selection_title), getString(R.string.english_ime_settings), }; - final Context context = this; final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface di, int position) { @@ -2380,7 +2367,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction switch (position) { case 0: Intent intent = CompatUtils.getInputLanguageSelectionIntent( - ImfUtils.getInputMethodIdOfThisIme(context), + mRichImm.getInputMethodIdOfThisIme(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 75b67bfc6..23887c4d5 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -26,7 +26,6 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.research.ResearchLogger; @@ -399,7 +398,7 @@ public final class RichInputConnection { if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } - public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { + public String getNthPreviousWord(final String sentenceSeperators, final int n) { mIC = mParent.getCurrentInputConnection(); if (null == mIC) return null; final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); @@ -467,19 +466,22 @@ public final class RichInputConnection { // (n = 2) "abc|" -> null // (n = 2) "abc |" -> null // (n = 2) "abc. def|" -> null - public static CharSequence getNthPreviousWord(final CharSequence prev, + public static String getNthPreviousWord(final CharSequence prev, final String sentenceSeperators, final int n) { if (prev == null) return null; - String[] w = spaceRegex.split(prev); + final String[] w = spaceRegex.split(prev); // If we can't find n words, or we found an empty word, return null. - if (w.length < n || w[w.length - n].length() <= 0) return null; + if (w.length < n) return null; + final String nthPrevWord = w[w.length - n]; + final int length = nthPrevWord.length(); + if (length <= 0) return null; // If ends in a separator, return null - char lastChar = w[w.length - n].charAt(w[w.length - n].length() - 1); + final char lastChar = nthPrevWord.charAt(length - 1); if (sentenceSeperators.contains(String.valueOf(lastChar))) return null; - return w[w.length - n]; + return nthPrevWord; } /** @@ -512,19 +514,20 @@ public final class RichInputConnection { * be included in the returned range * @return a range containing the text surrounding the cursor */ - public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) { + public Range getWordRangeAtCursor(final String sep, final int additionalPrecedingWordsCount) { mIC = mParent.getCurrentInputConnection(); if (mIC == null || sep == null) { return null; } - CharSequence before = mIC.getTextBeforeCursor(1000, 0); - CharSequence after = mIC.getTextAfterCursor(1000, 0); + final CharSequence before = mIC.getTextBeforeCursor(1000, 0); + final CharSequence after = mIC.getTextAfterCursor(1000, 0); if (before == null || after == null) { return null; } // Going backward, alternate skipping non-separators and separators until enough words // have been read. + int count = additionalPrecedingWordsCount; int start = before.length(); boolean isStoppingAtWhitespace = true; // toggles to indicate what to stop at while (true) { // see comments below for why this is guaranteed to halt @@ -541,7 +544,7 @@ public final class RichInputConnection { // isStoppingAtWhitespace is true every other time through the loop, // so additionalPrecedingWordsCount is guaranteed to become < 0, which // guarantees outer loop termination - if (isStoppingAtWhitespace && (--additionalPrecedingWordsCount < 0)) { + if (isStoppingAtWhitespace && (--count < 0)) { break; // outer loop } isStoppingAtWhitespace = !isStoppingAtWhitespace; @@ -559,7 +562,7 @@ public final class RichInputConnection { } } - int cursor = getCursorPosition(); + final int cursor = getCursorPosition(); if (start >= 0 && cursor + end <= after.length() + before.length()) { String word = before.toString().substring(start, before.length()) + after.toString().substring(0, end); @@ -570,8 +573,8 @@ public final class RichInputConnection { } public boolean isCursorTouchingWord(final SettingsValues settingsValues) { - CharSequence before = getTextBeforeCursor(1, 0); - CharSequence after = getTextAfterCursor(1, 0); + final CharSequence before = getTextBeforeCursor(1, 0); + final CharSequence after = getTextAfterCursor(1, 0); if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0)) && !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) { return true; @@ -587,7 +590,7 @@ public final class RichInputConnection { if (DEBUG_BATCH_NESTING) checkBatchEdit(); final CharSequence lastOne = getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 - && lastOne.charAt(0) == Keyboard.CODE_SPACE) { + && lastOne.charAt(0) == Constants.CODE_SPACE) { deleteSurroundingText(1, 0); } } @@ -613,7 +616,7 @@ public final class RichInputConnection { CharSequence word = getWordAtCursor(settings.mWordSeparators); // We don't suggest on leading single quotes, so we have to remove them from the word if // it starts with single quotes. - while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) { + while (!TextUtils.isEmpty(word) && Constants.CODE_SINGLE_QUOTE == word.charAt(0)) { word = word.subSequence(1, word.length()); } if (TextUtils.isEmpty(word)) return null; @@ -665,7 +668,7 @@ public final class RichInputConnection { // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to // enter surrogate pairs this code will have been removed. if (TextUtils.isEmpty(textBeforeCursor) - || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) { + || (Constants.CODE_SPACE != textBeforeCursor.charAt(1))) { // We may only come here if the application is changing the text while we are typing. // This is quite a broken case, but not logically impossible, so we shouldn't crash, // but some debugging log may be in order. diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java new file mode 100644 index 000000000..63dfd3250 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; + +import android.content.Context; +import android.os.IBinder; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; + +import java.util.Collections; +import java.util.List; + +/** + * Enrichment class for InputMethodManager to simplify interaction and add functionality. + */ +public final class RichInputMethodManager { + private static final String TAG = RichInputMethodManager.class.getSimpleName(); + + private RichInputMethodManager() { + // This utility class is not publicly instantiable. + } + + private static final RichInputMethodManager sInstance = new RichInputMethodManager(); + + private InputMethodManagerCompatWrapper mImmWrapper; + private InputMethodInfo mInputMethodInfoOfThisIme; + + public static RichInputMethodManager getInstance() { + sInstance.checkInitialized(); + return sInstance; + } + + public static void init(final Context context) { + sInstance.initInternal(context); + } + + private void checkInitialized() { + if (mImmWrapper == null) { + throw new RuntimeException(TAG + " is used before initialization"); + } + } + + private void initInternal(final Context context) { + mImmWrapper = new InputMethodManagerCompatWrapper(context); + mInputMethodInfoOfThisIme = getInputMethodInfoOfThisIme(context); + } + + public InputMethodManager getInputMethodManager() { + checkInitialized(); + return mImmWrapper.mImm; + } + + private InputMethodInfo getInputMethodInfoOfThisIme(final Context context) { + final String packageName = context.getPackageName(); + for (final InputMethodInfo imi : mImmWrapper.mImm.getInputMethodList()) { + if (imi.getPackageName().equals(packageName)) { + return imi; + } + } + throw new RuntimeException("Input method id for " + packageName + " not found."); + } + + public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { + final boolean result = mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme); + if (!result) { + mImmWrapper.mImm.switchToLastInputMethod(token); + return false; + } + return true; + } + + public InputMethodInfo getInputMethodInfoOfThisIme() { + return mInputMethodInfoOfThisIme; + } + + public String getInputMethodIdOfThisIme() { + return mInputMethodInfoOfThisIme.getId(); + } + + public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) { + return checkIfSubtypeBelongsToImeAndEnabled(mInputMethodInfoOfThisIme, subtype); + } + + public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled( + final InputMethodSubtype subtype) { + final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype); + final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList( + subtype, mImmWrapper.mImm.getEnabledInputMethodSubtypeList( + mInputMethodInfoOfThisIme, false /* allowsImplicitlySelectedSubtypes */)); + return subtypeEnabled && !subtypeExplicitlyEnabled; + } + + public boolean checkIfSubtypeBelongsToImeAndEnabled(final InputMethodInfo imi, + final InputMethodSubtype subtype) { + return checkIfSubtypeBelongsToList( + subtype, mImmWrapper.mImm.getEnabledInputMethodSubtypeList( + imi, true /* allowsImplicitlySelectedSubtypes */)); + } + + private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype, + final List<InputMethodSubtype> subtypes) { + for (final InputMethodSubtype ims : subtypes) { + if (ims.equals(subtype)) { + return true; + } + } + return false; + } + + public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype subtype) { + final InputMethodInfo myImi = mInputMethodInfoOfThisIme; + final int count = myImi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype ims = myImi.getSubtypeAt(i); + if (ims.equals(subtype)) { + return true; + } + } + return false; + } + + public InputMethodSubtype getCurrentInputMethodSubtype( + final InputMethodSubtype defaultSubtype) { + final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype(); + return (currentSubtype != null) ? currentSubtype : defaultSubtype; + } + + public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) { + final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList(); + return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis); + } + + public boolean hasMultipleEnabledSubtypesInThisIme( + final boolean shouldIncludeAuxiliarySubtypes) { + final List<InputMethodInfo> imiList = Collections.singletonList(mInputMethodInfoOfThisIme); + return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList); + } + + private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes, + final List<InputMethodInfo> imiList) { + // Number of the filtered IMEs + int filteredImisCount = 0; + + for (InputMethodInfo imi : imiList) { + // We can return true immediately after we find two or more filtered IMEs. + if (filteredImisCount > 1) return true; + final List<InputMethodSubtype> subtypes = + mImmWrapper.mImm.getEnabledInputMethodSubtypeList(imi, true); + // IMEs that have no subtypes should be counted. + if (subtypes.isEmpty()) { + ++filteredImisCount; + continue; + } + + int auxCount = 0; + for (InputMethodSubtype subtype : subtypes) { + 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; + } + } + + if (filteredImisCount > 1) { + return true; + } + final List<InputMethodSubtype> subtypes = + mImmWrapper.mImm.getEnabledInputMethodSubtypeList(null, true); + int keyboardCount = 0; + // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's + // both explicitly and implicitly enabled input method subtype. + // (The current IME should be LatinIME.) + for (InputMethodSubtype subtype : subtypes) { + if (KEYBOARD_MODE.equals(subtype.getMode())) { + ++keyboardCount; + } + } + return keyboardCount > 1; + } + + public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString, + final String keyboardLayoutSetName) { + final InputMethodInfo myImi = mInputMethodInfoOfThisIme; + final int count = myImi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype subtype = myImi.getSubtypeAt(i); + final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype); + if (localeString.equals(subtype.getLocale()) + && keyboardLayoutSetName.equals(layoutName)) { + return subtype; + } + } + return null; + } + + public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) { + mImmWrapper.mImm.setInputMethodAndSubtype( + token, mInputMethodInfoOfThisIme.getId(), subtype); + } + + public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) { + mImmWrapper.mImm.setAdditionalInputMethodSubtypes( + mInputMethodInfoOfThisIme.getId(), subtypes); + } +} diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 348928df8..fdad5430a 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -203,7 +203,8 @@ public final class Settings extends InputMethodSettingsFragment final Intent intent = dictionaryLink.getIntent(); final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); - if (0 >= number) { + // TODO: The experimental version is not supported by the Dictionary Pack Service yet + if (ProductionFlag.IS_EXPERIMENTAL || 0 >= number) { textCorrectionGroup.removePreference(dictionaryLink); } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 2a778aa0d..61536b573 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -355,16 +355,15 @@ public final class SettingsValues { return prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, true); } - public boolean isLanguageSwitchKeyEnabled(final Context context) { + public boolean isLanguageSwitchKeyEnabled() { if (!mShowsLanguageSwitchKey) { return false; } + final RichInputMethodManager imm = RichInputMethodManager.getInstance(); if (mIncludesOtherImesInLanguageSwitchList) { - return ImfUtils.hasMultipleEnabledIMEsOrSubtypes( - context, /* include aux subtypes */false); + return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */); } else { - return ImfUtils.hasMultipleEnabledSubtypesInThisIme( - context, /* include aux subtypes */false); + return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */); } } diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index df7709892..043043cef 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -16,10 +16,9 @@ package com.android.inputmethod.latin; +import android.text.InputType; import android.text.TextUtils; -import com.android.inputmethod.keyboard.Keyboard; // For character constants - import java.util.ArrayList; import java.util.Locale; @@ -28,30 +27,30 @@ public final class StringUtils { // This utility class is not publicly instantiable. } - public static int codePointCount(String text) { + public static int codePointCount(final String text) { if (TextUtils.isEmpty(text)) return 0; return text.codePointCount(0, text.length()); } - public static boolean containsInArray(String key, String[] array) { + public static boolean containsInArray(final String key, final String[] array) { for (final String element : array) { if (key.equals(element)) return true; } return false; } - public static boolean containsInCsv(String key, String csv) { + public static boolean containsInCsv(final String key, final String csv) { if (TextUtils.isEmpty(csv)) return false; return containsInArray(key, csv.split(",")); } - public static String appendToCsvIfNotExists(String key, String csv) { + public static String appendToCsvIfNotExists(final String key, final String csv) { if (TextUtils.isEmpty(csv)) return key; if (containsInCsv(key, csv)) return csv; return csv + "," + key; } - public static String removeFromCsvIfExists(String key, String csv) { + public static String removeFromCsvIfExists(final String key, final String csv) { if (TextUtils.isEmpty(csv)) return ""; final String[] elements = csv.split(","); if (!containsInArray(key, elements)) return csv; @@ -63,82 +62,20 @@ public final class StringUtils { } /** - * Returns true if a and b are equal ignoring the case of the character. - * @param a first character to check - * @param b second character to check - * @return {@code true} if a and b are equal, {@code false} otherwise. - */ - public static boolean equalsIgnoreCase(char a, char b) { - // Some language, such as Turkish, need testing both cases. - return a == b - || Character.toLowerCase(a) == Character.toLowerCase(b) - || Character.toUpperCase(a) == Character.toUpperCase(b); - } - - /** - * Returns true if a and b are equal ignoring the case of the characters, including if they are - * both null. - * @param a first CharSequence to check - * @param b second CharSequence to check - * @return {@code true} if a and b are equal, {@code false} otherwise. - */ - public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) { - if (a == b) - return true; // including both a and b are null. - if (a == null || b == null) - return false; - final int length = a.length(); - if (length != b.length()) - return false; - for (int i = 0; i < length; i++) { - if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) - return false; - } - return true; - } - - /** - * Returns true if a and b are equal ignoring the case of the characters, including if a is null - * and b is zero length. - * @param a CharSequence to check - * @param b character array to check - * @param offset start offset of array b - * @param length length of characters in array b - * @return {@code true} if a and b are equal, {@code false} otherwise. - * @throws IndexOutOfBoundsException - * if {@code offset < 0 || length < 0 || offset + length > data.length}. - * @throws NullPointerException if {@code b == null}. - */ - public static boolean equalsIgnoreCase(CharSequence a, char[] b, int offset, int length) { - if (offset < 0 || length < 0 || length > b.length - offset) - throw new IndexOutOfBoundsException("array.length=" + b.length + " offset=" + offset - + " length=" + length); - if (a == null) - return length == 0; // including a is null and b is zero length. - if (a.length() != length) - return false; - for (int i = 0; i < length; i++) { - if (!equalsIgnoreCase(a.charAt(i), b[offset + i])) - return false; - } - return true; - } - - /** * Remove duplicates from an array of strings. * * This method will always keep the first occurrence of all strings at their position * in the array, removing the subsequent ones. */ - public static void removeDupes(final ArrayList<CharSequence> suggestions) { + public static void removeDupes(final ArrayList<String> suggestions) { if (suggestions.size() < 2) return; int i = 1; // Don't cache suggestions.size(), since we may be removing items while (i < suggestions.size()) { - final CharSequence cur = suggestions.get(i); + final String cur = suggestions.get(i); // Compare each suggestion with each previous suggestion for (int j = 0; j < i; j++) { - CharSequence previous = suggestions.get(j); + final String previous = suggestions.get(j); if (TextUtils.equals(cur, previous)) { suggestions.remove(i); i--; @@ -149,7 +86,7 @@ public final class StringUtils { } } - public static String toTitleCase(String s, Locale locale) { + public static String toTitleCase(final String s, final Locale locale) { if (s.length() <= 1) { // TODO: is this really correct? Shouldn't this be s.toUpperCase()? return s; @@ -165,21 +102,19 @@ public final class StringUtils { return s.toUpperCase(locale).charAt(0) + s.substring(1); } + private static final int[] EMPTY_CODEPOINTS = {}; + public static int[] toCodePointArray(final String string) { - final char[] characters = string.toCharArray(); - final int length = characters.length; - final int[] codePoints = new int[Character.codePointCount(characters, 0, length)]; + final int length = string.length(); if (length <= 0) { - return new int[0]; + return EMPTY_CODEPOINTS; } - int codePoint = Character.codePointAt(characters, 0); - int dsti = 0; - for (int srci = Character.charCount(codePoint); - srci < length; srci += Character.charCount(codePoint), ++dsti) { - codePoints[dsti] = codePoint; - codePoint = Character.codePointAt(characters, srci); + final int[] codePoints = new int[string.codePointCount(0, length)]; + int destIndex = 0; + for (int index = 0; index < length; index = string.offsetByCodePoints(index, 1)) { + codePoints[destIndex] = string.codePointAt(index); + destIndex++; } - codePoints[dsti] = codePoint; return codePoints; } @@ -237,7 +172,7 @@ public final class StringUtils { } else { for (i = cs.length(); i > 0; i--) { final char c = cs.charAt(i - 1); - if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE + if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE && Character.getType(c) != Character.START_PUNCTUATION) { break; } @@ -253,11 +188,11 @@ public final class StringUtils { // if the first char that's not a space or tab is a start of line (as in \n, start of text, // or some other similar characters). int j = i; - char prevChar = Keyboard.CODE_SPACE; + char prevChar = Constants.CODE_SPACE; if (hasSpaceBefore) --j; while (j > 0) { prevChar = cs.charAt(j - 1); - if (!Character.isSpaceChar(prevChar) && prevChar != Keyboard.CODE_TAB) break; + if (!Character.isSpaceChar(prevChar) && prevChar != Constants.CODE_TAB) break; j--; } if (j <= 0 || Character.isWhitespace(prevChar)) { @@ -296,7 +231,7 @@ public final class StringUtils { // variants of English, the final period is placed within double quotes and maybe // other closing punctuation signs. This is generally not true in other languages. final char c = cs.charAt(j - 1); - if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE + if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE && Character.getType(c) != Character.END_PUNCTUATION) { break; } @@ -310,10 +245,10 @@ public final class StringUtils { // end of a sentence. If we have a question mark or an exclamation mark, it's the end of // a sentence. If it's neither, the only remaining case is the period so we get the opposite // case out of the way. - if (c == Keyboard.CODE_QUESTION_MARK || c == Keyboard.CODE_EXCLAMATION_MARK) { + if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) { return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes; } - if (c != Keyboard.CODE_PERIOD || j <= 0) { + if (c != Constants.CODE_PERIOD || j <= 0) { return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes; } @@ -363,7 +298,7 @@ public final class StringUtils { case WORD: if (Character.isLetter(c)) { state = WORD; - } else if (c == Keyboard.CODE_PERIOD) { + } else if (c == Constants.CODE_PERIOD) { state = PERIOD; } else { return caps; @@ -379,7 +314,7 @@ public final class StringUtils { case LETTER: if (Character.isLetter(c)) { state = LETTER; - } else if (c == Keyboard.CODE_PERIOD) { + } else if (c == Constants.CODE_PERIOD) { state = PERIOD; } else { return noCaps; diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java index 579f96bb4..5d8c0b17d 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java +++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java @@ -151,18 +151,19 @@ public final class SubtypeLocale { // InputMethodSubtype's display name in its locale. // isAdditionalSubtype (T=true, F=false) - // locale layout | display name - // ------ ------ - ---------------------- - // en_US qwerty F English (US) exception - // en_GB qwerty F English (UK) exception - // fr azerty F Français - // fr_CA qwerty F Français (Canada) - // de qwertz F Deutsch - // zz qwerty F No language (QWERTY) in system locale - // fr qwertz T Français (QWERTZ) - // de qwerty T Deutsch (QWERTY) - // en_US azerty T English (US) (AZERTY) - // zz azerty T No language (AZERTY) in system locale + // locale layout | display name + // ------ ------- - ---------------------- + // en_US qwerty F English (US) exception + // en_GB qwerty F English (UK) exception + // es_US spanish F Español (EE.UU.) exception + // fr azerty F Français + // fr_CA qwerty F Français (Canada) + // de qwertz F Deutsch + // zz qwerty F No language (QWERTY) in system locale + // fr qwertz T Français (QWERTZ) + // de qwerty T Deutsch (QWERTY) + // en_US azerty T English (US) (AZERTY) + // zz azerty T No language (AZERTY) in system locale public static String getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res) { final String replacementString = (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15 diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 8e51a372b..8f2e27549 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -43,7 +43,7 @@ public final class SubtypeSwitcher { private static final String TAG = SubtypeSwitcher.class.getSimpleName(); private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); - private /* final */ InputMethodManager mImm; + private /* final */ RichInputMethodManager mRichImm; private /* final */ Resources mResources; private /* final */ ConnectivityManager mConnectivityManager; @@ -84,7 +84,7 @@ public final class SubtypeSwitcher { public static void init(final Context context) { SubtypeLocale.init(context); sInstance.initialize(context); - sInstance.updateAllParameters(context); + sInstance.updateAllParameters(); } private SubtypeSwitcher() { @@ -93,13 +93,13 @@ public final class SubtypeSwitcher { private void initialize(final Context service) { mResources = service.getResources(); - mImm = ImfUtils.getInputMethodManager(service); + mRichImm = RichInputMethodManager.getInstance(); mConnectivityManager = (ConnectivityManager) service.getSystemService( Context.CONNECTIVITY_SERVICE); mCurrentSystemLocale = mResources.getConfiguration().locale; - mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet( - service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); - mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype); + mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); + mCurrentSubtype = mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype); if (mNoLanguageSubtype == null) { throw new RuntimeException("Can't find no lanugage with QWERTY subtype"); } @@ -110,9 +110,9 @@ public final class SubtypeSwitcher { // Update all parameters stored in SubtypeSwitcher. // Only configuration changed event is allowed to call this because this is heavy. - private void updateAllParameters(final Context context) { + private void updateAllParameters() { mCurrentSystemLocale = mResources.getConfiguration().locale; - updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype)); + updateSubtype(mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype)); updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); } @@ -137,7 +137,7 @@ public final class SubtypeSwitcher { */ private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) { final List<InputMethodSubtype> enabledSubtypesOfThisIme = - mImm.getEnabledInputMethodSubtypeList(null, true); + mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true); mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) { @@ -162,7 +162,7 @@ public final class SubtypeSwitcher { } // TODO: Update an icon for shortcut IME final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts = - mImm.getShortcutInputMethodsAndSubtypes(); + mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes(); mShortcutInputMethodInfo = null; mShortcutSubtype = null; for (final InputMethodInfo imi : shortcuts.keySet()) { @@ -193,8 +193,13 @@ public final class SubtypeSwitcher { } final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype); + final boolean sameLocale = mCurrentSystemLocale.equals(newLocale); + final boolean sameLanguage = mCurrentSystemLocale.getLanguage().equals( + newLocale.getLanguage()); + final boolean implicitlyEnabled = + mRichImm.checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype); mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage( - mCurrentSystemLocale.equals(newLocale)); + sameLocale || (sameLanguage && implicitlyEnabled)); if (newSubtype.equals(mCurrentSubtype)) return; @@ -221,7 +226,7 @@ public final class SubtypeSwitcher { if (token == null) { return; } - final InputMethodManager imm = mImm; + final InputMethodManager imm = mRichImm.getInputMethodManager(); new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { @@ -238,14 +243,8 @@ public final class SubtypeSwitcher { if (mShortcutSubtype == null) { return true; } - final boolean allowsImplicitlySelectedSubtypes = true; - for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList( - mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) { - if (enabledSubtype.equals(mShortcutSubtype)) { - return true; - } - } - return false; + return mRichImm.checkIfSubtypeBelongsToImeAndEnabled( + mShortcutInputMethodInfo, mShortcutSubtype); } public boolean isShortcutImeReady() { @@ -285,12 +284,12 @@ public final class SubtypeSwitcher { return SubtypeLocale.getSubtypeLocale(mCurrentSubtype); } - public boolean onConfigurationChanged(final Configuration conf, final Context context) { + public boolean onConfigurationChanged(final Configuration conf) { final Locale systemLocale = conf.locale; final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale); // If system configuration was changed, update all parameters. if (systemLocaleChanged) { - updateAllParameters(context); + updateAllParameters(); } return systemLocaleChanged; } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index f0e3b4ebd..3dc2ba95b 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -19,7 +19,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.text.TextUtils; -import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -38,7 +38,7 @@ public final class Suggest { public static final String TAG = Suggest.class.getSimpleName(); // Session id for - // {@link #getSuggestedWords(WordComposer,CharSequence,ProximityInfo,boolean,int)}. + // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}. public static final int SESSION_TYPING = 0; public static final int SESSION_GESTURE = 1; @@ -71,7 +71,8 @@ public final class Suggest { mLocale = locale; } - /* package for test */ Suggest(final Context context, final File dictionary, + @UsedForTesting + Suggest(final Context context, final File dictionary, final long startOffset, final long length, final Locale locale) { final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset, length /* useFullEditDistance */, false, locale); @@ -138,7 +139,7 @@ public final class Suggest { * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted * before the main dictionary, if set. This refers to the system-managed user dictionary. */ - public void setUserDictionary(UserBinaryDictionary userDictionary) { + public void setUserDictionary(final UserBinaryDictionary userDictionary) { addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary); } @@ -147,12 +148,12 @@ public final class Suggest { * the contacts dictionary by passing null to this method. In this case no contacts dictionary * won't be used. */ - public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) { + public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) { mContactsDict = contactsDictionary; addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary); } - public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) { + public void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) { addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); } @@ -160,9 +161,9 @@ public final class Suggest { mAutoCorrectionThreshold = threshold; } - public SuggestedWords getSuggestedWords( - final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) { + public SuggestedWords getSuggestedWords(final WordComposer wordComposer, + final String prevWordForBigram, final ProximityInfo proximityInfo, + final boolean isCorrectionEnabled, final int sessionId) { LatinImeLogger.onStartSuggestion(prevWordForBigram); if (wordComposer.isBatchMode()) { return getSuggestedWordsForBatchInput( @@ -174,9 +175,9 @@ public final class Suggest { } // Retrieves suggestions for the typing input. - private SuggestedWords getSuggestedWordsForTypingInput( - final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) { + private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer, + final String prevWordForBigram, final ProximityInfo proximityInfo, + final boolean isCorrectionEnabled) { final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS); @@ -203,7 +204,7 @@ public final class Suggest { wordComposerForLookup, prevWordForBigram, proximityInfo)); } - final CharSequence whitelistedWord; + final String whitelistedWord; if (suggestionsSet.isEmpty()) { whitelistedWord = null; } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) { @@ -287,9 +288,9 @@ public final class Suggest { } // Retrieves suggestions for the batch input. - private SuggestedWords getSuggestedWordsForBatchInput( - final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo, int sessionId) { + private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer, + final String prevWordForBigram, final ProximityInfo proximityInfo, + final int sessionId) { final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS); @@ -307,7 +308,7 @@ public final class Suggest { } for (SuggestedWordInfo wordInfo : suggestionsSet) { - LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict); + LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict); } final ArrayList<SuggestedWordInfo> suggestionsContainer = @@ -372,7 +373,7 @@ public final class Suggest { if (o1.mScore < o2.mScore) return 1; if (o1.mCodePointCount < o2.mCodePointCount) return -1; if (o1.mCodePointCount > o2.mCodePointCount) return 1; - return o1.mWord.toString().compareTo(o2.mWord.toString()); + return o1.mWord.compareTo(o2.mWord); } } private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator = @@ -383,16 +384,17 @@ public final class Suggest { final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); if (isAllUpperCase) { - sb.append(wordInfo.mWord.toString().toUpperCase(locale)); + sb.append(wordInfo.mWord.toUpperCase(locale)); } else if (isFirstCharCapitalized) { - sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale)); + sb.append(StringUtils.toTitleCase(wordInfo.mWord, locale)); } else { sb.append(wordInfo.mWord); } for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { - sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); + sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE); } - return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict); + return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind, + wordInfo.mSourceDict); } public void close() { diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 52e292a86..572f2906e 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -53,6 +53,10 @@ public final class SuggestedWords { mIsPrediction = isPrediction; } + public boolean isEmpty() { + return mSuggestedWordInfoList.isEmpty(); + } + public int size() { return mSuggestedWordInfoList.size(); } @@ -86,11 +90,14 @@ public final class SuggestedWords { public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions( final CompletionInfo[] infos) { final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList(); - for (CompletionInfo info : infos) { - if (null != info && info.getText() != null) { - result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE, - SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED)); - } + for (final CompletionInfo info : infos) { + if (info == null) continue; + final CharSequence text = info.getText(); + if (null == text) continue; + final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(), + SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED, + Dictionary.TYPE_APPLICATION_DEFINED); + result.add(suggestedWordInfo); } return result; } @@ -98,7 +105,7 @@ public final class SuggestedWords { // Should get rid of the first one (what the user typed previously) from suggestions // and replace it with what the user currently typed. public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions( - final CharSequence typedWord, final SuggestedWords previousSuggestions) { + final String typedWord, final SuggestedWords previousSuggestions) { final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList(); final HashSet<String> alreadySeen = CollectionUtils.newHashSet(); suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, @@ -107,7 +114,7 @@ public final class SuggestedWords { final int previousSize = previousSuggestions.size(); for (int pos = 1; pos < previousSize; pos++) { final SuggestedWordInfo prevWordInfo = previousSuggestions.getWordInfo(pos); - final String prevWord = prevWordInfo.mWord.toString(); + final String prevWord = prevWordInfo.mWord; // Filter out duplicate suggestion. if (!alreadySeen.contains(prevWord)) { suggestionsList.add(prevWordInfo); @@ -135,9 +142,9 @@ public final class SuggestedWords { public final String mSourceDict; private String mDebugString = ""; - public SuggestedWordInfo(final CharSequence word, final int score, final int kind, + public SuggestedWordInfo(final String word, final int score, final int kind, final String sourceDict) { - mWord = word.toString(); + mWord = word; mScore = score; mKind = kind; mSourceDict = sourceDict; @@ -145,7 +152,7 @@ public final class SuggestedWords { } - public void setDebugString(String str) { + public void setDebugString(final String str) { if (null == str) throw new NullPointerException("Debug info is null"); mDebugString = str; } @@ -167,7 +174,7 @@ public final class SuggestedWords { if (TextUtils.isEmpty(mDebugString)) { return mWord; } else { - return mWord + " (" + mDebugString.toString() + ")"; + return mWord + " (" + mDebugString + ")"; } } diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java index 8f21b7b4a..ec4dc1436 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java @@ -33,13 +33,13 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB @Override public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, - final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { + final String prevWordForBigrams, final ProximityInfo proximityInfo) { syncReloadDictionaryIfRequired(); return super.getSuggestions(codes, prevWordForBigrams, proximityInfo); } @Override - public synchronized boolean isValidWord(CharSequence word) { + public synchronized boolean isValidWord(final String word) { syncReloadDictionaryIfRequired(); return isValidWordInner(word); } diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java index 612f54d73..4bdaf2039 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java @@ -36,13 +36,13 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic @Override public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, - final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { + final String prevWordForBigrams, final ProximityInfo proximityInfo) { syncReloadDictionaryIfRequired(); return super.getSuggestions(codes, prevWordForBigrams, proximityInfo); } @Override - public synchronized boolean isValidWord(CharSequence word) { + public synchronized boolean isValidWord(final String word) { syncReloadDictionaryIfRequired(); return isValidWordInner(word); } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 60e6fa127..00c3cbe0a 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -200,7 +200,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { mContext.startActivity(intent); } - private void addWords(Cursor cursor) { + private void addWords(final Cursor cursor) { // 16 is JellyBean, but we want this to compile against ICS. final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16; clearFusionDictionary(); diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java index e39011145..787197755 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface; @@ -129,7 +130,8 @@ public final class UserHistoryDictIOUtils { /** * Constructs a new FusionDictionary from BigramDictionaryInterface. */ - /* packages for test */ static FusionDictionary constructFusionDictionary( + @UsedForTesting + static FusionDictionary constructFusionDictionary( final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) { final FusionDictionary fusionDict = new FusionDictionary(new Node(), new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false, @@ -193,7 +195,8 @@ public final class UserHistoryDictIOUtils { /** * Adds all unigrams and bigrams in maps to OnAddWordListener. */ - /* package for test */ static void addWordsFromWordMap(final Map<Integer, String> unigrams, + @UsedForTesting + static void addWordsFromWordMap(final Map<Integer, String> unigrams, final Map<Integer, Integer> frequencies, final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) { for (Map.Entry<Integer, String> entry : unigrams.entrySet()) { diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index 3615fa1fb..4fd9bfafb 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -21,6 +21,7 @@ import android.content.SharedPreferences; import android.os.AsyncTask; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface; @@ -75,7 +76,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { private final SharedPreferences mPrefs; // Should always be false except when we use this class for test - /* package for test */ boolean isTest = false; + @UsedForTesting boolean isTest = false; private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>> sLangDictCache = CollectionUtils.newConcurrentHashMap(); @@ -122,7 +123,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { @Override protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { + final String prevWord, final ProximityInfo proximityInfo) { // Inhibit suggestions (not predictions) for user history for now. Removing this method // is enough to use it through the standard ExpandableDictionary way. return null; @@ -132,7 +133,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { * Return whether the passed charsequence is in the dictionary. */ @Override - public synchronized boolean isValidWord(final CharSequence word) { + public synchronized boolean isValidWord(final String word) { // TODO: figure out what is the correct thing to do here. return false; } @@ -145,7 +146,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { * context, as in beginning of a sentence for example. * The second word may not be null (a NullPointerException would be thrown). */ - public int addToUserHistory(final String word1, String word2, boolean isValid) { + public int addToUserHistory(final String word1, final String word2, final boolean isValid) { if (word2.length() >= BinaryDictionary.MAX_WORD_LENGTH || (word1 != null && word1.length() >= BinaryDictionary.MAX_WORD_LENGTH)) { return -1; @@ -175,7 +176,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { return -1; } - public boolean cancelAddingUserHistory(String word1, String word2) { + public boolean cancelAddingUserHistory(final String word1, final String word2) { if (mBigramListLock.tryLock()) { try { if (mBigramList.removeBigram(word1, word2)) { @@ -226,7 +227,8 @@ public final class UserHistoryDictionary extends ExpandableDictionary { final ExpandableDictionary dictionary = this; final OnAddWordListener listener = new OnAddWordListener() { @Override - public void setUnigram(String word, String shortcutTarget, int frequency) { + public void setUnigram(final String word, final String shortcutTarget, + final int frequency) { profTotal++; if (DBG_SAVE_RESTORE) { Log.d(TAG, "load unigram: " + word + "," + frequency); @@ -236,7 +238,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { } @Override - public void setBigram(String word1, String word2, int frequency) { + public void setBigram(final String word1, final String word2, final int frequency) { if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) { profTotal++; @@ -250,7 +252,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { mBigramList.addBigram(word1, word2, (byte)frequency); } }; - + // Load the dictionary from binary file FileInputStream inStream = null; try { @@ -292,8 +294,9 @@ public final class UserHistoryDictionary extends ExpandableDictionary { private final SharedPreferences mPrefs; private final Context mContext; - public UpdateBinaryTask(UserHistoryDictionaryBigramList pendingWrites, String locale, - UserHistoryDictionary dict, SharedPreferences prefs, Context context) { + public UpdateBinaryTask(final UserHistoryDictionaryBigramList pendingWrites, + final String locale, final UserHistoryDictionary dict, + final SharedPreferences prefs, final Context context) { mBigramList = pendingWrites; mLocale = locale; mUserHistoryDictionary = dict; @@ -303,7 +306,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { } @Override - protected Void doInBackground(Void... v) { + protected Void doInBackground(final Void... v) { if (mUserHistoryDictionary.isTest) { // If isTest == true, wait until the lock is released. mUserHistoryDictionary.mBigramListLock.lock(); @@ -360,7 +363,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { } @Override - public int getFrequency(String word1, String word2) { + public int getFrequency(final String word1, final String word2) { final int freq; if (word1 == null) { // unigram freq = FREQUENCY_FOR_TYPED; @@ -390,6 +393,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary { } } + @UsedForTesting void forceAddWordForTest(final String word1, final String word2, final boolean isValid) { mBigramListLock.lock(); try { diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index da0071adc..daff442f3 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -38,7 +38,7 @@ public final class WordComposer { private int[] mPrimaryKeyCodes; private final InputPointers mInputPointers = new InputPointers(N); private final StringBuilder mTypedWord; - private CharSequence mAutoCorrection; + private String mAutoCorrection; private boolean mIsResumed; private boolean mIsBatchMode; @@ -64,7 +64,7 @@ public final class WordComposer { refreshSize(); } - public WordComposer(WordComposer source) { + public WordComposer(final WordComposer source) { mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length); mTypedWord = new StringBuilder(source.mTypedWord); mInputPointers.copy(source.mInputPointers); @@ -121,7 +121,8 @@ public final class WordComposer { return mInputPointers; } - private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) { + private static boolean isFirstCharCapitalized(final int index, final int codePoint, + final boolean previous) { if (index == 0) return Character.isUpperCase(codePoint); return previous && !Character.isUpperCase(codePoint); } @@ -129,12 +130,12 @@ public final class WordComposer { /** * Add a new keystroke, with the pressed key's code point with the touch point coordinates. */ - public void add(int primaryCode, int keyX, int keyY) { + public void add(final int primaryCode, final int keyX, final int keyY) { final int newIndex = size(); mTypedWord.appendCodePoint(primaryCode); refreshSize(); if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { - mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE + mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE ? Character.toLowerCase(primaryCode) : primaryCode; // In the batch input mode, the {@code mInputPointers} holds batch input points and // shouldn't be overridden by the "typed key" coordinates @@ -148,7 +149,7 @@ public final class WordComposer { newIndex, primaryCode, mIsFirstCharCapitalized); if (Character.isUpperCase(primaryCode)) mCapsCount++; if (Character.isDigit(primaryCode)) mDigitsCount++; - if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) { + if (Constants.CODE_SINGLE_QUOTE == primaryCode) { ++mTrailingSingleQuotesCount; } else { mTrailingSingleQuotesCount = 0; @@ -156,12 +157,12 @@ public final class WordComposer { mAutoCorrection = null; } - public void setBatchInputPointers(InputPointers batchPointers) { + public void setBatchInputPointers(final InputPointers batchPointers) { mInputPointers.set(batchPointers); mIsBatchMode = true; } - public void setBatchInputWord(CharSequence word) { + public void setBatchInputWord(final String word) { reset(); mIsBatchMode = true; final int length = word.length(); @@ -235,7 +236,7 @@ public final class WordComposer { int i = mTypedWord.length(); while (i > 0) { i = mTypedWord.offsetByCodePoints(i, -1); - if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; + if (Constants.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; ++mTrailingSingleQuotesCount; } } @@ -321,14 +322,14 @@ public final class WordComposer { /** * Sets the auto-correction for this word. */ - public void setAutoCorrection(final CharSequence correction) { + public void setAutoCorrection(final String correction) { mAutoCorrection = correction; } /** * @return the auto-correction for this word, or null if none. */ - public CharSequence getAutoCorrectionOrNull() { + public String getAutoCorrectionOrNull() { return mAutoCorrection; } @@ -341,7 +342,7 @@ public final class WordComposer { // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. public LastComposedWord commitWord(final int type, final String committedWord, - final String separatorString, final CharSequence prevWord) { + final String separatorString, final String prevWord) { // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate // the last composed word to ensure this does not happen. diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 7b0231a6b..ee0e9cd7e 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -16,19 +16,32 @@ package com.android.inputmethod.latin.makedict; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.Map; import java.util.Stack; public final class BinaryDictIOUtils { private static final boolean DBG = false; + private static final int MSB24 = 0x800000; + private static final int SINT24_MAX = 0x7FFFFF; + private static final int MAX_JUMPS = 10000; + + private BinaryDictIOUtils() { + // This utility class is not publicly instantiable. + } private static final class Position { public static final int NOT_READ_GROUPCOUNT = -1; @@ -90,7 +103,9 @@ public final class BinaryDictIOUtils { final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags, formatOptions); - if (!isMovedGroup + final boolean isDeletedGroup = BinaryDictInputOutput.isDeletedGroup(info.mFlags, + formatOptions); + if (!isMovedGroup && !isDeletedGroup && info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word words.put(info.mOriginalAddress, new String(pushedChars, 0, index)); frequencies.put(info.mOriginalAddress, info.mFrequency); @@ -153,6 +168,7 @@ public final class BinaryDictIOUtils { * @throws IOException * @throws UnsupportedFormatException */ + @UsedForTesting public static int getTerminalPosition(final FusionDictionaryBufferInterface buffer, final String word) throws IOException, UnsupportedFormatException { if (word == null) return FormatSpec.NOT_VALID_WORD; @@ -165,19 +181,19 @@ public final class BinaryDictIOUtils { if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD; do { - int groupOffset = buffer.position() - header.mHeaderSize; final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer); - groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount); - boolean foundNextCharGroup = false; for (int i = 0; i < charGroupCount; ++i) { final int charGroupPos = buffer.position(); final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer, buffer.position(), header.mFormatOptions); - if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags, - header.mFormatOptions)) { - continue; - } + final boolean isMovedGroup = + BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags, + header.mFormatOptions); + final boolean isDeletedGroup = + BinaryDictInputOutput.isDeletedGroup(currentInfo.mFlags, + header.mFormatOptions); + if (isMovedGroup) continue; boolean same = true; for (int p = 0, j = word.offsetByCodePoints(0, wordPos); p < currentInfo.mCharacters.length; @@ -192,7 +208,8 @@ public final class BinaryDictIOUtils { if (same) { // found the group matches the word. if (wordPos + currentInfo.mCharacters.length == wordLen) { - if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) { + if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL + || isDeletedGroup) { return FormatSpec.NOT_VALID_WORD; } else { return charGroupPos; @@ -206,7 +223,6 @@ public final class BinaryDictIOUtils { buffer.position(currentInfo.mChildrenAddress); break; } - groupOffset = currentInfo.mEndAddress; } // If we found the next char group, it is under the file pointer. @@ -228,6 +244,10 @@ public final class BinaryDictIOUtils { return FormatSpec.NOT_VALID_WORD; } + private static int markAsDeleted(final int flags) { + return (flags & (~FormatSpec.MASK_GROUP_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED; + } + /** * Delete the word from the binary file. * @@ -236,6 +256,7 @@ public final class BinaryDictIOUtils { * @throws IOException * @throws UnsupportedFormatException */ + @UsedForTesting public static void deleteWord(final FusionDictionaryBufferInterface buffer, final String word) throws IOException, UnsupportedFormatException { buffer.position(0); @@ -245,21 +266,58 @@ public final class BinaryDictIOUtils { buffer.position(wordPosition); final int flags = buffer.readUnsignedByte(); - final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL; buffer.position(wordPosition); - buffer.put((byte)newFlags); + buffer.put((byte)markAsDeleted(flags)); } - private static void putSInt24(final FusionDictionaryBufferInterface buffer, + /** + * @return the size written, in bytes. Always 3 bytes. + */ + private static int writeSInt24ToBuffer(final FusionDictionaryBufferInterface buffer, final int value) { final int absValue = Math.abs(value); buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF)); buffer.put((byte)((absValue >> 8) & 0xFF)); buffer.put((byte)(absValue & 0xFF)); + return 3; + } + + /** + * @return the size written, in bytes. Always 3 bytes. + */ + private static int writeSInt24ToStream(final OutputStream destination, final int value) + throws IOException { + final int absValue = Math.abs(value); + destination.write((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF)); + destination.write((byte)((absValue >> 8) & 0xFF)); + destination.write((byte)(absValue & 0xFF)); + return 3; } /** - * Update a parent address in a CharGroup that is addressed by groupOriginAddress. + * @return the size written, in bytes. 1, 2, or 3 bytes. + */ + private static int writeVariableAddress(final OutputStream destination, final int value) + throws IOException { + switch (BinaryDictInputOutput.getByteSize(value)) { + case 1: + destination.write((byte)value); + break; + case 2: + destination.write((byte)(0xFF & (value >> 8))); + destination.write((byte)(0xFF & value)); + break; + case 3: + destination.write((byte)(0xFF & (value >> 16))); + destination.write((byte)(0xFF & (value >> 8))); + destination.write((byte)(0xFF & value)); + break; + } + return BinaryDictInputOutput.getByteSize(value); + } + + /** + * Update a parent address in a CharGroup that is referred to by groupOriginAddress. * * @param buffer the buffer to write. * @param groupOriginAddress the address of the group. @@ -275,8 +333,648 @@ public final class BinaryDictIOUtils { throw new RuntimeException("this file format does not support parent addresses"); } final int flags = buffer.readUnsignedByte(); + if (BinaryDictInputOutput.isMovedGroup(flags, formatOptions)) { + // if the group is moved, the parent address is stored in the destination group. + // We are guaranteed to process the destination group later, so there is no need to + // update anything here. + buffer.position(originalPosition); + return; + } + if (DBG) { + MakedictLog.d("update parent address flags=" + flags + ", " + groupOriginAddress); + } final int parentOffset = newParentAddress - groupOriginAddress; - putSInt24(buffer, parentOffset); + writeSInt24ToBuffer(buffer, parentOffset); + buffer.position(originalPosition); + } + + private static void skipCharGroup(final FusionDictionaryBufferInterface buffer, + final FormatOptions formatOptions) { + final int flags = buffer.readUnsignedByte(); + BinaryDictInputOutput.readParentAddress(buffer, formatOptions); + skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0); + BinaryDictInputOutput.readChildrenAddress(buffer, flags, formatOptions); + if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte(); + if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) { + final int shortcutsSize = buffer.readUnsignedShort(); + buffer.position(buffer.position() + shortcutsSize + - FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE); + } + if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) { + int bigramCount = 0; + while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) { + final int bigramFlags = buffer.readUnsignedByte(); + switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) { + case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: + buffer.readUnsignedByte(); + break; + case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: + buffer.readUnsignedShort(); + break; + case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES: + buffer.readUnsignedInt24(); + break; + } + if ((bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT) == 0) break; + } + if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) { + throw new RuntimeException("Too many bigrams in a group."); + } + } + } + + /** + * Update parent addresses in a Node that is referred to by nodeOriginAddress. + * + * @param buffer the buffer to be modified. + * @param nodeOriginAddress the address of a modified Node. + * @param newParentAddress the address to be written. + * @param formatOptions file format options. + */ + public static void updateParentAddresses(final FusionDictionaryBufferInterface buffer, + final int nodeOriginAddress, final int newParentAddress, + final FormatOptions formatOptions) { + final int originalPosition = buffer.position(); + buffer.position(nodeOriginAddress); + do { + final int count = BinaryDictInputOutput.readCharGroupCount(buffer); + for (int i = 0; i < count; ++i) { + updateParentAddress(buffer, buffer.position(), newParentAddress, formatOptions); + skipCharGroup(buffer, formatOptions); + } + final int forwardLinkAddress = buffer.readUnsignedInt24(); + buffer.position(forwardLinkAddress); + } while (formatOptions.mSupportsDynamicUpdate + && buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS); + buffer.position(originalPosition); + } + + private static void skipString(final FusionDictionaryBufferInterface buffer, + final boolean hasMultipleChars) { + if (hasMultipleChars) { + int character = CharEncoding.readChar(buffer); + while (character != FormatSpec.INVALID_CHARACTER) { + character = CharEncoding.readChar(buffer); + } + } else { + CharEncoding.readChar(buffer); + } + } + + /** + * Write a string to a stream. + * + * @param destination the stream to write. + * @param word the string to be written. + * @return the size written, in bytes. + * @throws IOException + */ + private static int writeString(final OutputStream destination, final String word) + throws IOException { + int size = 0; + final int length = word.length(); + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = word.codePointAt(i); + if (CharEncoding.getCharSize(codePoint) == 1) { + destination.write((byte)codePoint); + size++; + } else { + destination.write((byte)(0xFF & (codePoint >> 16))); + destination.write((byte)(0xFF & (codePoint >> 8))); + destination.write((byte)(0xFF & codePoint)); + size += 3; + } + } + destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR); + size += FormatSpec.GROUP_TERMINATOR_SIZE; + return size; + } + + /** + * Update a children address in a CharGroup that is addressed by groupOriginAddress. + * + * @param buffer the buffer to write. + * @param groupOriginAddress the address of the group. + * @param newChildrenAddress the absolute address of the child. + * @param formatOptions file format options. + */ + public static void updateChildrenAddress(final FusionDictionaryBufferInterface buffer, + final int groupOriginAddress, final int newChildrenAddress, + final FormatOptions formatOptions) { + final int originalPosition = buffer.position(); + buffer.position(groupOriginAddress); + final int flags = buffer.readUnsignedByte(); + final int parentAddress = BinaryDictInputOutput.readParentAddress(buffer, formatOptions); + skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0); + if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte(); + final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS + ? FormatSpec.NO_CHILDREN_ADDRESS : newChildrenAddress - buffer.position(); + writeSInt24ToBuffer(buffer, childrenOffset); buffer.position(originalPosition); } + + /** + * Write a char group to an output stream. + * A char group is an in-memory representation of a node in trie. + * A char group info is an on-disk representation of a node. + * + * @param destination the stream to write. + * @param info the char group info to be written. + * @return the size written, in bytes. + */ + public static int writeCharGroup(final OutputStream destination, final CharGroupInfo info) + throws IOException { + int size = FormatSpec.GROUP_FLAGS_SIZE; + destination.write((byte)info.mFlags); + final int parentOffset = info.mParentAddress == FormatSpec.NO_PARENT_ADDRESS ? + FormatSpec.NO_PARENT_ADDRESS : info.mParentAddress - info.mOriginalAddress; + size += writeSInt24ToStream(destination, parentOffset); + + for (int i = 0; i < info.mCharacters.length; ++i) { + if (CharEncoding.getCharSize(info.mCharacters[i]) == 1) { + destination.write((byte)info.mCharacters[i]); + size++; + } else { + size += writeSInt24ToStream(destination, info.mCharacters[i]); + } + } + if (info.mCharacters.length > 1) { + destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR); + size++; + } + + if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) { + destination.write((byte)info.mFrequency); + size++; + } + + if (DBG) { + MakedictLog.d("writeCharGroup origin=" + info.mOriginalAddress + ", size=" + size + + ", child=" + info.mChildrenAddress + ", characters =" + + new String(info.mCharacters, 0, info.mCharacters.length)); + } + final int childrenOffset = info.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS ? + 0 : info.mChildrenAddress - (info.mOriginalAddress + size); + writeSInt24ToStream(destination, childrenOffset); + size += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; + + if (info.mShortcutTargets != null && info.mShortcutTargets.size() > 0) { + final int shortcutListSize = + BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets); + destination.write((byte)(shortcutListSize >> 8)); + destination.write((byte)(shortcutListSize & 0xFF)); + size += 2; + final Iterator<WeightedString> shortcutIterator = info.mShortcutTargets.iterator(); + while (shortcutIterator.hasNext()) { + final WeightedString target = shortcutIterator.next(); + destination.write((byte)BinaryDictInputOutput.makeShortcutFlags( + shortcutIterator.hasNext(), target.mFrequency)); + size++; + size += writeString(destination, target.mWord); + } + } + + if (info.mBigrams != null) { + // TODO: Consolidate this code with the code that computes the size of the bigram list + // in BinaryDictionaryInputOutput#computeActualNodeSize + for (int i = 0; i < info.mBigrams.size(); ++i) { + + final int bigramFrequency = info.mBigrams.get(i).mFrequency; + int bigramFlags = (i < info.mBigrams.size() - 1) + ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0; + size++; + final int bigramOffset = info.mBigrams.get(i).mAddress - (info.mOriginalAddress + + size); + bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0; + switch (BinaryDictInputOutput.getByteSize(bigramOffset)) { + case 1: + bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE; + break; + case 2: + bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES; + break; + case 3: + bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES; + break; + } + bigramFlags |= bigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY; + destination.write((byte)bigramFlags); + size += writeVariableAddress(destination, Math.abs(bigramOffset)); + } + } + return size; + } + + @SuppressWarnings("unused") + private static void updateForwardLink(final FusionDictionaryBufferInterface buffer, + final int nodeOriginAddress, final int newNodeAddress, + final FormatOptions formatOptions) { + buffer.position(nodeOriginAddress); + int jumpCount = 0; + while (jumpCount++ < MAX_JUMPS) { + final int count = BinaryDictInputOutput.readCharGroupCount(buffer); + for (int i = 0; i < count; ++i) skipCharGroup(buffer, formatOptions); + final int forwardLinkAddress = buffer.readUnsignedInt24(); + if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) { + buffer.position(buffer.position() - FormatSpec.FORWARD_LINK_ADDRESS_SIZE); + writeSInt24ToBuffer(buffer, newNodeAddress); + return; + } + buffer.position(forwardLinkAddress); + } + if (DBG && jumpCount >= MAX_JUMPS) { + throw new RuntimeException("too many jumps, probably a bug."); + } + } + + /** + * Helper method to move a char group to the tail of the file. + */ + private static int moveCharGroup(final OutputStream destination, + final FusionDictionaryBufferInterface buffer, final CharGroupInfo info, + final int nodeOriginAddress, final int oldGroupAddress, + final FormatOptions formatOptions) throws IOException { + updateParentAddress(buffer, oldGroupAddress, buffer.limit() + 1, formatOptions); + buffer.position(oldGroupAddress); + final int currentFlags = buffer.readUnsignedByte(); + buffer.position(oldGroupAddress); + buffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags + & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG)))); + int size = FormatSpec.GROUP_FLAGS_SIZE; + updateForwardLink(buffer, nodeOriginAddress, buffer.limit(), formatOptions); + size += writeNode(destination, new CharGroupInfo[] { info }); + return size; + } + + /** + * Compute the size of the char group. + */ + private static int computeGroupSize(final CharGroupInfo info, + final FormatOptions formatOptions) { + int size = FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE + + BinaryDictInputOutput.getGroupCharactersSize(info.mCharacters) + + BinaryDictInputOutput.getChildrenAddressSize(info.mFlags, formatOptions); + if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) { + size += FormatSpec.GROUP_FREQUENCY_SIZE; + } + if (info.mShortcutTargets != null && !info.mShortcutTargets.isEmpty()) { + size += BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets); + } + if (info.mBigrams != null) { + for (final PendingAttribute attr : info.mBigrams) { + size += FormatSpec.GROUP_FLAGS_SIZE; + size += BinaryDictInputOutput.getByteSize(attr.mAddress); + } + } + return size; + } + + /** + * Write a node to the stream. + * + * @param destination the stream to write. + * @param infos groups to be written. + * @return the size written, in bytes. + * @throws IOException + */ + private static int writeNode(final OutputStream destination, final CharGroupInfo[] infos) + throws IOException { + int size = BinaryDictInputOutput.getGroupCountSize(infos.length); + switch (BinaryDictInputOutput.getGroupCountSize(infos.length)) { + case 1: + destination.write((byte)infos.length); + break; + case 2: + destination.write((byte)(infos.length >> 8)); + destination.write((byte)(infos.length & 0xFF)); + break; + default: + throw new RuntimeException("Invalid group count size."); + } + for (final CharGroupInfo info : infos) size += writeCharGroup(destination, info); + writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS); + return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE; + } + + /** + * Move a group that is referred to by oldGroupOrigin to the tail of the file. + * And set the children address to the byte after the group. + * + * @param nodeOrigin the address of the tail of the file. + * @param characters + * @param length + * @param flags + * @param frequency + * @param parentAddress + * @param shortcutTargets + * @param bigrams + * @param destination the stream representing the tail of the file. + * @param buffer the buffer representing the (constant-size) body of the file. + * @param oldNodeOrigin + * @param oldGroupOrigin + * @param formatOptions + * @return the size written, in bytes. + * @throws IOException + */ + private static int moveGroup(final int nodeOrigin, final int[] characters, final int length, + final int flags, final int frequency, final int parentAddress, + final ArrayList<WeightedString> shortcutTargets, + final ArrayList<PendingAttribute> bigrams, final OutputStream destination, + final FusionDictionaryBufferInterface buffer, final int oldNodeOrigin, + final int oldGroupOrigin, final FormatOptions formatOptions) throws IOException { + int size = 0; + final int newGroupOrigin = nodeOrigin + 1; + final int[] writtenCharacters = Arrays.copyOfRange(characters, 0, length); + final CharGroupInfo tmpInfo = new CharGroupInfo(newGroupOrigin, -1 /* endAddress */, + flags, writtenCharacters, frequency, parentAddress, FormatSpec.NO_CHILDREN_ADDRESS, + shortcutTargets, bigrams); + size = computeGroupSize(tmpInfo, formatOptions); + final CharGroupInfo newInfo = new CharGroupInfo(newGroupOrigin, newGroupOrigin + size, + flags, writtenCharacters, frequency, parentAddress, + nodeOrigin + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets, + bigrams); + moveCharGroup(destination, buffer, newInfo, oldNodeOrigin, oldGroupOrigin, formatOptions); + return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE; + } + + /** + * Insert a word into a binary dictionary. + * + * @param buffer + * @param destination + * @param word + * @param frequency + * @param bigramStrings + * @param shortcuts + * @throws IOException + * @throws UnsupportedFormatException + */ + // TODO: Support batch insertion. + // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary. + @UsedForTesting + public static void insertWord(final FusionDictionaryBufferInterface buffer, + final OutputStream destination, final String word, final int frequency, + final ArrayList<WeightedString> bigramStrings, + final ArrayList<WeightedString> shortcuts, final boolean isNotAWord, + final boolean isBlackListEntry) + throws IOException, UnsupportedFormatException { + final ArrayList<PendingAttribute> bigrams = new ArrayList<PendingAttribute>(); + if (bigramStrings != null) { + for (final WeightedString bigram : bigramStrings) { + int position = getTerminalPosition(buffer, bigram.mWord); + if (position == FormatSpec.NOT_VALID_WORD) { + // TODO: figure out what is the correct thing to do here. + } else { + bigrams.add(new PendingAttribute(bigram.mFrequency, position)); + } + } + } + + final boolean isTerminal = true; + final boolean hasBigrams = !bigrams.isEmpty(); + final boolean hasShortcuts = shortcuts != null && !shortcuts.isEmpty(); + + // find the insert position of the word. + if (buffer.position() != 0) buffer.position(0); + final FileHeader header = BinaryDictInputOutput.readHeader(buffer); + + int wordPos = 0, address = buffer.position(), nodeOriginAddress = buffer.position(); + final int[] codePoints = FusionDictionary.getCodePoints(word); + final int wordLen = codePoints.length; + + for (int depth = 0; depth < Constants.Dictionary.MAX_WORD_LENGTH; ++depth) { + if (wordPos >= wordLen) break; + nodeOriginAddress = buffer.position(); + int nodeParentAddress = -1; + final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer); + boolean foundNextGroup = false; + + for (int i = 0; i < charGroupCount; ++i) { + address = buffer.position(); + final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer, + buffer.position(), header.mFormatOptions); + final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags, + header.mFormatOptions); + if (isMovedGroup) continue; + nodeParentAddress = (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) + ? FormatSpec.NO_PARENT_ADDRESS : currentInfo.mParentAddress + address; + boolean matched = true; + for (int p = 0; p < currentInfo.mCharacters.length; ++p) { + if (wordPos + p >= wordLen) { + /* + * splitting + * before + * abcd - ef + * + * insert "abc" + * + * after + * abc - d - ef + */ + final int newNodeAddress = buffer.limit(); + final int flags = BinaryDictInputOutput.makeCharGroupFlags(p > 1, + isTerminal, 0, hasShortcuts, hasBigrams, false /* isNotAWord */, + false /* isBlackListEntry */, header.mFormatOptions); + int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p, flags, + frequency, nodeParentAddress, shortcuts, bigrams, destination, + buffer, nodeOriginAddress, address, header.mFormatOptions); + + final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p, + currentInfo.mCharacters.length); + if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { + updateParentAddresses(buffer, currentInfo.mChildrenAddress, + newNodeAddress + written + 1, header.mFormatOptions); + } + final CharGroupInfo newInfo2 = new CharGroupInfo( + newNodeAddress + written + 1, -1 /* endAddress */, + currentInfo.mFlags, characters2, currentInfo.mFrequency, + newNodeAddress + 1, currentInfo.mChildrenAddress, + currentInfo.mShortcutTargets, currentInfo.mBigrams); + writeNode(destination, new CharGroupInfo[] { newInfo2 }); + return; + } else if (codePoints[wordPos + p] != currentInfo.mCharacters[p]) { + if (p > 0) { + /* + * splitting + * before + * ab - cd + * + * insert "ac" + * + * after + * a - b - cd + * | + * - c + */ + + final int newNodeAddress = buffer.limit(); + final int childrenAddress = currentInfo.mChildrenAddress; + + // move prefix + final int prefixFlags = BinaryDictInputOutput.makeCharGroupFlags(p > 1, + false /* isTerminal */, 0 /* childrenAddressSize*/, + false /* hasShortcut */, false /* hasBigrams */, + false /* isNotAWord */, false /* isBlackListEntry */, + header.mFormatOptions); + int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p, + prefixFlags, -1 /* frequency */, nodeParentAddress, null, null, + destination, buffer, nodeOriginAddress, address, + header.mFormatOptions); + + final int[] suffixCharacters = Arrays.copyOfRange( + currentInfo.mCharacters, p, currentInfo.mCharacters.length); + if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { + updateParentAddresses(buffer, currentInfo.mChildrenAddress, + newNodeAddress + written + 1, header.mFormatOptions); + } + final int suffixFlags = BinaryDictInputOutput.makeCharGroupFlags( + suffixCharacters.length > 1, + (currentInfo.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0, + 0 /* childrenAddressSize */, + (currentInfo.mFlags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) + != 0, + (currentInfo.mFlags & FormatSpec.FLAG_HAS_BIGRAMS) != 0, + isNotAWord, isBlackListEntry, header.mFormatOptions); + final CharGroupInfo suffixInfo = new CharGroupInfo( + newNodeAddress + written + 1, -1 /* endAddress */, suffixFlags, + suffixCharacters, currentInfo.mFrequency, newNodeAddress + 1, + currentInfo.mChildrenAddress, currentInfo.mShortcutTargets, + currentInfo.mBigrams); + written += computeGroupSize(suffixInfo, header.mFormatOptions) + 1; + + final int[] newCharacters = Arrays.copyOfRange(codePoints, wordPos + p, + codePoints.length); + final int flags = BinaryDictInputOutput.makeCharGroupFlags( + newCharacters.length > 1, isTerminal, + 0 /* childrenAddressSize */, hasShortcuts, hasBigrams, + isNotAWord, isBlackListEntry, header.mFormatOptions); + final CharGroupInfo newInfo = new CharGroupInfo( + newNodeAddress + written, -1 /* endAddress */, flags, + newCharacters, frequency, newNodeAddress + 1, + FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams); + writeNode(destination, new CharGroupInfo[] { suffixInfo, newInfo }); + return; + } + matched = false; + break; + } + } + + if (matched) { + if (wordPos + currentInfo.mCharacters.length == wordLen) { + // the word exists in the dictionary. + // only update group. + final int newNodeAddress = buffer.limit(); + final boolean hasMultipleChars = currentInfo.mCharacters.length > 1; + final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars, + isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams, + isNotAWord, isBlackListEntry, header.mFormatOptions); + final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1, + -1 /* endAddress */, flags, currentInfo.mCharacters, frequency, + nodeParentAddress, currentInfo.mChildrenAddress, shortcuts, + bigrams); + moveCharGroup(destination, buffer, newInfo, nodeOriginAddress, address, + header.mFormatOptions); + return; + } + wordPos += currentInfo.mCharacters.length; + if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) { + /* + * found the prefix of the word. + * make new node and link to the node from this group. + * + * before + * ab - cd + * + * insert "abcde" + * + * after + * ab - cd - e + */ + final int newNodeAddress = buffer.limit(); + updateChildrenAddress(buffer, address, newNodeAddress, + header.mFormatOptions); + final int newGroupAddress = newNodeAddress + 1; + final boolean hasMultipleChars = (wordLen - wordPos) > 1; + final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars, + isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams, + isNotAWord, isBlackListEntry, header.mFormatOptions); + final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen); + final CharGroupInfo newInfo = new CharGroupInfo(newGroupAddress, -1, flags, + characters, frequency, address, FormatSpec.NO_CHILDREN_ADDRESS, + shortcuts, bigrams); + writeNode(destination, new CharGroupInfo[] { newInfo }); + return; + } + buffer.position(currentInfo.mChildrenAddress); + foundNextGroup = true; + break; + } + } + + if (foundNextGroup) continue; + + // reached the end of the array. + final int linkAddressPosition = buffer.position(); + int nextLink = buffer.readUnsignedInt24(); + if ((nextLink & MSB24) != 0) { + nextLink = -(nextLink & SINT24_MAX); + } + if (nextLink == FormatSpec.NO_FORWARD_LINK_ADDRESS) { + /* + * expand this node. + * + * before + * ab - cd + * + * insert "abef" + * + * after + * ab - cd + * | + * - ef + */ + + // change the forward link address. + final int newNodeAddress = buffer.limit(); + buffer.position(linkAddressPosition); + writeSInt24ToBuffer(buffer, newNodeAddress); + + final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen); + final int flags = BinaryDictInputOutput.makeCharGroupFlags(characters.length > 1, + isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams, + isNotAWord, isBlackListEntry, header.mFormatOptions); + final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1, + -1 /* endAddress */, flags, characters, frequency, nodeParentAddress, + FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams); + writeNode(destination, new CharGroupInfo[]{ newInfo }); + return; + } else { + depth--; + buffer.position(nextLink); + } + } + } + + /** + * Find a word from the buffer. + * + * @param buffer the buffer representing the body of the dictionary file. + * @param word the word searched + * @return the found group + * @throws IOException + * @throws UnsupportedFormatException + */ + @UsedForTesting + public static CharGroupInfo findWordFromBuffer(final FusionDictionaryBufferInterface buffer, + final String word) throws IOException, UnsupportedFormatException { + int position = getTerminalPosition(buffer, word); + if (position != FormatSpec.NOT_VALID_WORD) { + buffer.position(0); + final FileHeader header = BinaryDictInputOutput.readHeader(buffer); + buffer.position(position); + return BinaryDictInputOutput.readCharGroup(buffer, position, header.mFormatOptions); + } + return null; + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index b431a4da9..d1a3c7b0a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin.makedict; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; @@ -76,12 +77,12 @@ public final class BinaryDictInputOutput { @Override public int readUnsignedByte() { - return ((int)mBuffer.get()) & 0xFF; + return mBuffer.get() & 0xFF; } @Override public int readUnsignedShort() { - return ((int)mBuffer.getShort()) & 0xFFFF; + return mBuffer.getShort() & 0xFFFF; } @Override @@ -124,8 +125,7 @@ public final class BinaryDictInputOutput { /** * A class grouping utility function for our specific character encoding. */ - private static final class CharEncoding { - + static final class CharEncoding { private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF; @@ -154,7 +154,7 @@ public final class BinaryDictInputOutput { * @param character the character code. * @return the size in binary encoded-form, either 1 or 3 bytes. */ - private static int getCharSize(final int character) { + static int getCharSize(final int character) { // See char encoding in FusionDictionary.java if (fitsOnOneByte(character)) return 1; if (FormatSpec.INVALID_CHARACTER == character) return 1; @@ -263,7 +263,7 @@ public final class BinaryDictInputOutput { * @param buffer the buffer, positioned over an encoded character. * @return the character code. */ - private static int readChar(final FusionDictionaryBufferInterface buffer) { + static int readChar(final FusionDictionaryBufferInterface buffer) { int character = buffer.readUnsignedByte(); if (!fitsOnOneByte(character)) { if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) { @@ -277,6 +277,21 @@ public final class BinaryDictInputOutput { } /** + * Compute the binary size of the character array. + * + * 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 characters the character array + * @return the size of the char array, including the terminator if any + */ + static int getGroupCharactersSize(final int[] characters) { + int size = CharEncoding.getCharArraySize(characters); + if (characters.length > 1) size += FormatSpec.GROUP_TERMINATOR_SIZE; + return size; + } + + /** * 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 @@ -286,9 +301,7 @@ public final class BinaryDictInputOutput { * @return the size of the char array, including the terminator if any */ private static int getGroupCharactersSize(final CharGroup group) { - int size = CharEncoding.getCharArraySize(group.mChars); - if (group.hasSeveralChars()) size += FormatSpec.GROUP_TERMINATOR_SIZE; - return size; + return getGroupCharactersSize(group.mChars); } /** @@ -338,7 +351,7 @@ public final class BinaryDictInputOutput { * This is known in advance and does not change according to position in the file * like address lists do. */ - private static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) { + static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) { if (null == shortcutList) return 0; int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE; for (final WeightedString shortcut : shortcutList) { @@ -399,7 +412,16 @@ public final class BinaryDictInputOutput { * Helper method to check whether the group is moved. */ public static boolean isMovedGroup(final int flags, final FormatOptions options) { - return options.mSupportsDynamicUpdate && ((flags & FormatSpec.FLAG_IS_MOVED) == 1); + return options.mSupportsDynamicUpdate + && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED); + } + + /** + * Helper method to check whether the group is deleted. + */ + public static boolean isDeletedGroup(final int flags, final FormatOptions formatOptions) { + return formatOptions.mSupportsDynamicUpdate + && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED); } /** @@ -439,7 +461,7 @@ public final class BinaryDictInputOutput { * @param address the address * @return the byte size. */ - private static int getByteSize(final int address) { + static int getByteSize(final int address) { assert(address <= UINT24_MAX); if (!hasChildrenAddress(address)) { return 0; @@ -452,11 +474,8 @@ public final class BinaryDictInputOutput { } } - private static final int SINT8_MAX = 0x7F; - private static final int SINT16_MAX = 0x7FFF; private static final int SINT24_MAX = 0x7FFFFF; private static final int MSB8 = 0x80; - private static final int MSB16 = 0x8000; private static final int MSB24 = 0x800000; // End utility methods. @@ -721,53 +740,60 @@ public final class BinaryDictInputOutput { return 3; } - private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress, - final int childrenOffset, final FormatOptions formatOptions) { + /** + * Makes the flag value for a char group. + * + * @param hasMultipleChars whether the group has multiple chars. + * @param isTerminal whether the group is terminal. + * @param childrenAddressSize the size of a children address. + * @param hasShortcuts whether the group has shortcuts. + * @param hasBigrams whether the group has bigrams. + * @param isNotAWord whether the group is not a word. + * @param isBlackListEntry whether the group is a blacklist entry. + * @param formatOptions file format options. + * @return the flags + */ + static int makeCharGroupFlags(final boolean hasMultipleChars, final boolean isTerminal, + final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams, + final boolean isNotAWord, final boolean isBlackListEntry, + final FormatOptions formatOptions) { byte flags = 0; - if (group.mChars.length > 1) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS; - if (group.mFrequency >= 0) { - flags |= FormatSpec.FLAG_IS_TERMINAL; - } - if (null != group.mChildren) { - final int byteSize = formatOptions.mSupportsDynamicUpdate - ? FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE : getByteSize(childrenOffset); - switch (byteSize) { - case 1: - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE; - break; - case 2: - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES; - break; - case 3: - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; - break; - default: - throw new RuntimeException("Node with a strange address"); - } - } else if (formatOptions.mSupportsDynamicUpdate) { - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; - } - if (null != group.mShortcutTargets) { - if (DBG && 0 == group.mShortcutTargets.size()) { - throw new RuntimeException("0-sized shortcut list must be null"); - } - flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS; - } - if (null != group.mBigrams) { - if (DBG && 0 == group.mBigrams.size()) { - throw new RuntimeException("0-sized bigram list must be null"); + if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS; + if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL; + if (formatOptions.mSupportsDynamicUpdate) { + flags |= FormatSpec.FLAG_IS_NOT_MOVED; + } else if (true) { + switch (childrenAddressSize) { + case 1: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE; + break; + case 2: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES; + break; + case 3: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; + break; + case 0: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS; + break; + default: + throw new RuntimeException("Node with a strange address"); } - flags |= FormatSpec.FLAG_HAS_BIGRAMS; - } - if (group.mIsNotAWord) { - flags |= FormatSpec.FLAG_IS_NOT_A_WORD; - } - if (group.mIsBlacklistEntry) { - flags |= FormatSpec.FLAG_IS_BLACKLISTED; } + if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS; + if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS; + if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD; + if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED; return flags; } + private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress, + final int childrenOffset, final FormatOptions formatOptions) { + return (byte) makeCharGroupFlags(group.mChars.length > 1, group.mFrequency >= 0, + getByteSize(childrenOffset), group.mShortcutTargets != null, group.mBigrams != null, + group.mIsNotAWord, group.mIsBlacklistEntry, formatOptions); + } + /** * Makes the flag value for a bigram. * @@ -859,7 +885,7 @@ public final class BinaryDictInputOutput { * @param frequency the frequency of the attribute, 0..15 * @return the flags */ - private static final int makeShortcutFlags(final boolean more, final int frequency) { + static final int makeShortcutFlags(final boolean more, final int frequency) { return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0) + (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY); } @@ -897,6 +923,7 @@ public final class BinaryDictInputOutput { */ private static int writePlacedNode(final FusionDictionary dict, byte[] buffer, final Node node, final FormatOptions formatOptions) { + // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup int index = node.mCachedAddress; final int groupCount = node.mData.size(); @@ -1178,7 +1205,7 @@ public final class BinaryDictInputOutput { // Input methods: Read a binary dictionary to memory. // readDictionaryBinary is the public entry point for them. - private static int getChildrenAddressSize(final int optionFlags, + static int getChildrenAddressSize(final int optionFlags, final FormatOptions formatOptions) { if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) { @@ -1194,7 +1221,7 @@ public final class BinaryDictInputOutput { } } - private static int readChildrenAddress(final FusionDictionaryBufferInterface buffer, + static int readChildrenAddress(final FusionDictionaryBufferInterface buffer, final int optionFlags, final FormatOptions options) { if (options.mSupportsDynamicUpdate) { final int address = buffer.readUnsignedInt24(); @@ -1219,7 +1246,7 @@ public final class BinaryDictInputOutput { } } - private static int readParentAddress(final FusionDictionaryBufferInterface buffer, + static int readParentAddress(final FusionDictionaryBufferInterface buffer, final FormatOptions formatOptions) { if (supportsDynamicUpdate(formatOptions)) { final int parentAddress = buffer.readUnsignedInt24(); @@ -1290,7 +1317,8 @@ public final class BinaryDictInputOutput { ArrayList<PendingAttribute> bigrams = null; if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { bigrams = new ArrayList<PendingAttribute>(); - while (true) { + int bigramCount = 0; + while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) { final int bigramFlags = buffer.readUnsignedByte(); ++addressPointer; final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE) @@ -1318,6 +1346,9 @@ public final class BinaryDictInputOutput { bigramAddress)); if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break; } + if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) { + MakedictLog.d("too many bigrams in a group."); + } } return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency, parentAddress, childrenAddress, shortcutTargets, bigrams); @@ -1340,7 +1371,8 @@ public final class BinaryDictInputOutput { // of this method. Since it performs direct, unbuffered random access to the file and // may be called hundreds of thousands of times, the resulting performance is not // reasonable without some kind of cache. Thus: - private static TreeMap<Integer, String> wordCache = new TreeMap<Integer, String>(); + private static TreeMap<Integer, WeightedString> wordCache = + new TreeMap<Integer, WeightedString>(); /** * Finds, as a string, the word at the address passed as an argument. * @@ -1348,15 +1380,15 @@ public final class BinaryDictInputOutput { * @param headerSize the size of the header. * @param address the address to seek. * @param formatOptions file format options. - * @return the word, as a string. + * @return the word with its frequency, as a weighted string. */ - /* packages for tests */ static String getWordAtAddress( + /* package for tests */ static WeightedString getWordAtAddress( final FusionDictionaryBufferInterface buffer, final int headerSize, final int address, final FormatOptions formatOptions) { - final String cachedString = wordCache.get(address); + final WeightedString cachedString = wordCache.get(address); if (null != cachedString) return cachedString; - final String result; + final WeightedString result; final int originalPointer = buffer.position(); buffer.position(address); @@ -1372,14 +1404,17 @@ public final class BinaryDictInputOutput { return result; } + // TODO: static!? This will behave erratically when used in multi-threaded code. + // We need to fix this private static int[] sGetWordBuffer = new int[FormatSpec.MAX_WORD_LENGTH]; - private static String getWordAtAddressWithParentAddress( + private static WeightedString getWordAtAddressWithParentAddress( final FusionDictionaryBufferInterface buffer, final int headerSize, final int address, final FormatOptions options) { final StringBuilder builder = new StringBuilder(); int currentAddress = address; int index = FormatSpec.MAX_WORD_LENGTH - 1; + int frequency = Integer.MIN_VALUE; // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) { CharGroupInfo currentInfo; @@ -1394,6 +1429,7 @@ public final class BinaryDictInputOutput { MakedictLog.d("Too many jumps - probably a bug"); } } while (isMovedGroup(currentInfo.mFlags, options)); + if (Integer.MIN_VALUE == frequency) frequency = currentInfo.mFrequency; for (int i = 0; i < currentInfo.mCharacters.length; ++i) { sGetWordBuffer[index--] = currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1]; @@ -1402,17 +1438,19 @@ public final class BinaryDictInputOutput { currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress; } - return new String(sGetWordBuffer, index + 1, FormatSpec.MAX_WORD_LENGTH - index - 1); + return new WeightedString( + new String(sGetWordBuffer, index + 1, FormatSpec.MAX_WORD_LENGTH - index - 1), + frequency); } - private static String getWordAtAddressWithoutParentAddress( + private static WeightedString getWordAtAddressWithoutParentAddress( final FusionDictionaryBufferInterface buffer, final int headerSize, final int address, final FormatOptions options) { buffer.position(headerSize); final int count = readCharGroupCount(buffer); int groupOffset = getGroupCountSize(count); final StringBuilder builder = new StringBuilder(); - String result = null; + WeightedString result = null; CharGroupInfo last = null; for (int i = count - 1; i >= 0; --i) { @@ -1420,7 +1458,7 @@ public final class BinaryDictInputOutput { groupOffset = info.mEndAddress; if (info.mOriginalAddress == address) { builder.append(new String(info.mCharacters, 0, info.mCharacters.length)); - result = builder.toString(); + result = new WeightedString(builder.toString(), info.mFrequency); break; // and return } if (hasChildrenAddress(info.mChildrenAddress)) { @@ -1481,9 +1519,11 @@ public final class BinaryDictInputOutput { if (null != info.mBigrams) { bigrams = new ArrayList<WeightedString>(); for (PendingAttribute bigram : info.mBigrams) { - final String word = getWordAtAddress( + final WeightedString word = getWordAtAddress( buffer, headerSize, bigram.mAddress, options); - bigrams.add(new WeightedString(word, bigram.mFrequency)); + final int reconstructedFrequency = + reconstructBigramFrequency(word.mFrequency, bigram.mFrequency); + bigrams.add(new WeightedString(word.mWord, reconstructedFrequency)); } } if (hasChildrenAddress(info.mChildrenAddress)) { @@ -1618,6 +1658,7 @@ public final class BinaryDictInputOutput { * @param dict an optional dictionary to add words to, or null. * @return the created (or merged) dictionary. */ + @UsedForTesting public static FusionDictionary readDictionaryBinary( final FusionDictionaryBufferInterface buffer, final FusionDictionary dict) throws IOException, UnsupportedFormatException { @@ -1655,17 +1696,24 @@ public final class BinaryDictInputOutput { } /** + * Helper method to pass a file name instead of a File object to isBinaryDictionary. + */ + public static boolean isBinaryDictionary(final String filename) { + final File file = new File(filename); + return isBinaryDictionary(file); + } + + /** * 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. + * @param file The file to test. * @return true if it's a binary dictionary, false otherwise */ - public static boolean isBinaryDictionary(final String filename) { + public static boolean isBinaryDictionary(final File file) { FileInputStream inStream = null; try { - final File file = new File(filename); inStream = new FileInputStream(file); final ByteBuffer buffer = inStream.getChannel().map( FileChannel.MapMode.READ_ONLY, 0, file.length()); @@ -1700,8 +1748,7 @@ public final class BinaryDictInputOutput { final int bigramFrequency) { final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY); - final float resultFreqFloat = (float)unigramFrequency - + stepSize * (bigramFrequency + 1.0f); + final float resultFreqFloat = unigramFrequency + stepSize * (bigramFrequency + 1.0f); return (int)resultFreqFloat; } } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index b3fbb9fb5..705f66414 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -59,9 +59,11 @@ public final class FormatSpec { * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES * g | ELSE - * s | is moved ? 2 bits, 11 = no - * | 01 = yes + * s | is moved ? 2 bits, 11 = no : FLAG_IS_NOT_MOVED + * | This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES + * | 01 = yes : FLAG_IS_MOVED * | the new address is stored in the same place as the parent address + * | is deleted? 10 = yes : FLAG_IS_DELETED * | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS * | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS @@ -170,6 +172,7 @@ public final class FormatSpec { static final int PARENT_ADDRESS_SIZE = 3; static final int FORWARD_LINK_ADDRESS_SIZE = 3; + // These flags are used only in the static dictionary. static final int MASK_GROUP_ADDRESS_TYPE = 0xC0; static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00; static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40; @@ -183,7 +186,13 @@ public final class FormatSpec { static final int FLAG_HAS_BIGRAMS = 0x04; static final int FLAG_IS_NOT_A_WORD = 0x02; static final int FLAG_IS_BLACKLISTED = 0x01; - static final int FLAG_IS_MOVED = 0x40; + + // These flags are used only in the dynamic dictionary. + static final int MASK_MOVE_AND_DELETE_FLAG = 0xC0; + static final int FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE = 0x40; + static final int FLAG_IS_MOVED = 0x00 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE; + static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE; + static final int FLAG_IS_DELETED = 0x80; static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80; static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40; @@ -210,10 +219,13 @@ public final class FormatSpec { static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127 static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767 + static final int MAX_BIGRAMS_IN_A_GROUP = 10000; static final int MAX_TERMINAL_FREQUENCY = 255; static final int MAX_BIGRAM_FREQUENCY = 15; + public static final int SHORTCUT_WHITELIST_FREQUENCY = 15; + // This option needs to be the same numeric value as the one in binary_format.h. static final int NOT_VALID_WORD = -99; static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3; diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 3193ef457..b0b3777df 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -21,6 +21,7 @@ import com.android.inputmethod.latin.Constants; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -141,6 +142,33 @@ public final class FusionDictionary implements Iterable<Word> { return NOT_A_TERMINAL != mFrequency; } + public int getFrequency() { + return mFrequency; + } + + public boolean getIsNotAWord() { + return mIsNotAWord; + } + + public boolean getIsBlacklistEntry() { + return mIsBlacklistEntry; + } + + public ArrayList<WeightedString> getShortcutTargets() { + // We don't want write permission to escape outside the package, so we return a copy + if (null == mShortcutTargets) return null; + final ArrayList<WeightedString> copyOfShortcutTargets = + new ArrayList<WeightedString>(mShortcutTargets); + return copyOfShortcutTargets; + } + + public ArrayList<WeightedString> getBigrams() { + // We don't want write permission to escape outside the package, so we return a copy + if (null == mBigrams) return null; + final ArrayList<WeightedString> copyOfBigrams = new ArrayList<WeightedString>(mBigrams); + return copyOfBigrams; + } + public boolean hasSeveralChars() { assert(mChars.length > 0); return 1 < mChars.length; @@ -249,8 +277,6 @@ public final class FusionDictionary implements Iterable<Word> { /** * Options global to the dictionary. - * - * There are no options at the moment, so this class is empty. */ public static final class DictionaryOptions { public final boolean mGermanUmlautProcessing; @@ -262,6 +288,43 @@ public final class FusionDictionary implements Iterable<Word> { mGermanUmlautProcessing = germanUmlautProcessing; mFrenchLigatureProcessing = frenchLigatureProcessing; } + @Override + public String toString() { // Convenience method + return toString(0, false); + } + public String toString(final int indentCount, final boolean plumbing) { + final StringBuilder indent = new StringBuilder(); + if (plumbing) { + indent.append("H:"); + } else { + for (int i = 0; i < indentCount; ++i) { + indent.append(" "); + } + } + final StringBuilder s = new StringBuilder(); + for (final String optionKey : mAttributes.keySet()) { + s.append(indent); + s.append(optionKey); + s.append(" = "); + if ("date".equals(optionKey) && !plumbing) { + // Date needs a number of milliseconds, but the dictionary contains seconds + s.append(new Date( + 1000 * Long.parseLong(mAttributes.get(optionKey))).toString()); + } else { + s.append(mAttributes.get(optionKey)); + } + s.append("\n"); + } + if (mGermanUmlautProcessing) { + s.append(indent); + s.append("Needs German umlaut processing\n"); + } + if (mFrenchLigatureProcessing) { + s.append(indent); + s.append("Needs French ligature processing\n"); + } + return s.toString(); + } } public final DictionaryOptions mOptions; @@ -279,7 +342,7 @@ public final class FusionDictionary implements Iterable<Word> { /** * Helper method to convert a String to an int array. */ - static private int[] getCodePoints(final String word) { + static int[] getCodePoints(final String word) { // TODO: this is a copy-paste of the contents of StringUtils.toCodePointArray, // which is not visible from the makedict package. Factor this code. final char[] characters = word.toCharArray(); @@ -358,6 +421,10 @@ public final class FusionDictionary implements Iterable<Word> { if (charGroup2 == null) { add(getCodePoints(word2), 0, null, false /* isNotAWord */, false /* isBlacklistEntry */); + // The chargroup for the first word may have moved by the above insertion, + // if word1 and word2 share a common stem that happens not to have been + // a cutting point until now. In this case, we need to refresh charGroup. + charGroup = findWordInTree(mRoot, word1); } charGroup.addBigram(word2, frequency); } else { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 5a11ae534..49b98863f 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -212,7 +212,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService } } - private final ArrayList<CharSequence> mSuggestions; + private final ArrayList<String> mSuggestions; private final int[] mScores; private final String mOriginalText; private final float mSuggestionThreshold; @@ -335,7 +335,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY); final int bestScore = mScores[mLength - 1]; - final CharSequence bestSuggestion = mSuggestions.get(0); + final String bestSuggestion = mSuggestions.get(0); final float normalizedScore = BinaryDictionary.calcNormalizedScore( mOriginalText, bestSuggestion.toString(), bestScore); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 53ed4d3c3..a8f323999 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -268,7 +268,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { dictInfo.mDictionary.getSuggestions(composer, prevWord, dictInfo.mProximityInfo); for (final SuggestedWordInfo suggestion : suggestions) { - final String suggestionStr = suggestion.mWord.toString(); + final String suggestionStr = suggestion.mWord; suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0, suggestionStr.length(), suggestion.mScore); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 1fb2bbb6a..eae5d2e60 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -51,11 +51,11 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> new Dictionary(Dictionary.TYPE_MAIN) { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { + final String prevWord, final ProximityInfo proximityInfo) { return noSuggestions; } @Override - public boolean isValidWord(CharSequence word) { + public boolean isValidWord(final String word) { // This is never called. However if for some strange reason it ever gets // called, returning true is less destructive (it will not underline the // word in red). diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index 11bb97031..6c0d79c2b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin.spellcheck; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; @@ -23,7 +24,7 @@ import com.android.inputmethod.latin.Constants; import java.util.TreeMap; public final class SpellCheckerProximityInfo { - /* public for test */ + @UsedForTesting final public static int NUL = Constants.NOT_A_CODE; // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 4e9fd1968..35d5a0067 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -22,7 +22,6 @@ import android.graphics.drawable.Drawable; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; @@ -66,7 +65,7 @@ public final class MoreSuggestions extends Keyboard { int pos = fromPos, rowStartPos = fromPos; final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS); while (pos < size) { - final String word = suggestions.getWord(pos).toString(); + final String word = suggestions.getWord(pos); // TODO: Should take care of text x-scaling. mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding; final int numColumn = pos - rowStartPos + 1; @@ -176,11 +175,11 @@ public final class MoreSuggestions extends Keyboard { } public Builder layout(final SuggestedWords suggestions, final int fromPos, - final int maxWidth, final int minWidth, final int maxRow) { - final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard(); + final int maxWidth, final int minWidth, final int maxRow, + final Keyboard parentKeyboard) { final int xmlId = R.xml.kbd_suggestions_pane_template; - load(xmlId, keyboard.mId); - mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2; + load(xmlId, parentKeyboard.mId); + mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight); final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow, diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java index 03a2e73d1..6cdd9e2cd 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java @@ -56,17 +56,17 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP final KeyboardActionListener mSuggestionsPaneListener = new KeyboardActionListener.Adapter() { @Override - public void onPressKey(int primaryCode) { + public void onPressKey(final int primaryCode) { mListener.onPressKey(primaryCode); } @Override - public void onReleaseKey(int primaryCode, boolean withSliding) { + public void onReleaseKey(final int primaryCode, final boolean withSliding) { mListener.onReleaseKey(primaryCode, withSliding); } @Override - public void onCodeInput(int primaryCode, int x, int y) { + public void onCodeInput(final int primaryCode, final int x, final int y) { final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE; if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) { mListener.onCustomRequest(index); @@ -79,11 +79,12 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP } }; - public MoreSuggestionsView(Context context, AttributeSet attrs) { + public MoreSuggestionsView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.moreSuggestionsViewStyle); } - public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) { + public MoreSuggestionsView(final Context context, final AttributeSet attrs, + final int defStyle) { super(context, attrs, defStyle); final Resources res = context.getResources(); @@ -94,7 +95,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Keyboard keyboard = getKeyboard(); if (keyboard != null) { final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); @@ -110,7 +111,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP } @Override - public void setKeyboard(Keyboard keyboard) { + public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); mModalPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop()); mSlidingPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), @@ -138,15 +139,16 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP } @Override - public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { + public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) { // Suggestions pane needs no pop-up key preview displayed, so we pass always false with a // delay of 0. The delay does not matter actually since the popup is not shown anyway. super.setKeyPreviewPopupEnabled(false, 0); } @Override - public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY, - PopupWindow window, KeyboardActionListener listener) { + public void showMoreKeysPanel(final View parentView, final Controller controller, + final int pointX, final int pointY, final PopupWindow window, + final KeyboardActionListener listener) { mController = controller; mListener = listener; final View container = (View)getParent(); @@ -179,12 +181,12 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP } @Override - public int translateX(int x) { + public int translateX(final int x) { return x - mOriginX; } @Override - public int translateY(int y) { + public int translateY(final int y) { return y - mOriginY; } @@ -211,7 +213,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP }; @Override - public boolean onTouchEvent(MotionEvent me) { + public boolean onTouchEvent(final MotionEvent me) { final int action = me.getAction(); final long eventTime = me.getEventTime(); final int index = me.getActionIndex(); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index e926fa209..e7cb97fc2 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -52,13 +52,16 @@ import android.widget.PopupWindow; import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.ResourceUtils; @@ -74,7 +77,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick OnLongClickListener { public interface Listener { public boolean addWordToUserDictionary(String word); - public void pickSuggestionManually(int index, CharSequence word); + public void pickSuggestionManually(int index, String word); } // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. @@ -83,7 +86,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mSuggestionsStrip; - private KeyboardView mKeyboardView; + KeyboardView mKeyboardView; private final View mMoreSuggestionsContainer; private final MoreSuggestionsView mMoreSuggestionsView; @@ -97,8 +100,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private final PopupWindow mPreviewPopup; private final TextView mPreviewText; - private Listener mListener; - private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; + Listener mListener; + SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; private final SuggestionStripViewParams mParams; private static final float MIN_TEXT_XSCALE = 0.70f; @@ -108,12 +111,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> { private static final int MSG_HIDE_PREVIEW = 0; - public UiHandler(SuggestionStripView outerInstance) { + public UiHandler(final SuggestionStripView outerInstance) { super(outerInstance); } @Override - public void dispatchMessage(Message msg) { + public void dispatchMessage(final Message msg) { final SuggestionStripView suggestionStripView = getOuterInstance(); switch (msg.what) { case MSG_HIDE_PREVIEW: @@ -177,8 +180,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private final TextView mLeftwardsArrowView; private final TextView mHintToSaveView; - public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle, - ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) { + public SuggestionStripViewParams(final Context context, final AttributeSet attrs, + final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers, + final ArrayList<TextView> infos) { mWords = words; mDividers = dividers; mInfos = infos; @@ -250,7 +254,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; } - public int setMoreSuggestionsHeight(int remainingHeight) { + public int setMoreSuggestionsHeight(final int remainingHeight) { final int currentHeight = getMoreSuggestionsHeight(); if (currentHeight <= remainingHeight) { return currentHeight; @@ -262,7 +266,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return newHeight; } - private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) { + private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, + final int color) { final Paint paint = new Paint(); paint.setAntiAlias(true); paint.setTextAlign(Align.CENTER); @@ -279,8 +284,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return new BitmapDrawable(res, buffer); } - private CharSequence getStyledSuggestionWord(SuggestedWords suggestedWords, int pos) { - final CharSequence word = suggestedWords.getWord(pos); + private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords, + final int pos) { + final String word = suggestedWords.getWord(pos); final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect(); final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid; if (!isAutoCorrect && !isTypedWordValid) @@ -299,7 +305,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return spannedWord; } - private int getWordPosition(int index, SuggestedWords suggestedWords) { + private int getWordPosition(final int index, final SuggestedWords suggestedWords) { // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more // suggestions. final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0; @@ -312,7 +318,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - private int getSuggestionTextColor(int index, SuggestedWords suggestedWords, int pos) { + private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords, + final int pos) { // TODO: Need to revisit this logic with bigram suggestions final boolean isSuggested = (pos != 0); @@ -331,7 +338,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // is in slot 1. if (index == mCenterSuggestionIndex && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet( - suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) { + suggestedWords.getWord(1), suggestedWords.getWord(0))) { return 0xFFFF0000; } } @@ -355,8 +362,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick params.gravity = Gravity.CENTER; } - public void layout(SuggestedWords suggestedWords, ViewGroup stripView, ViewGroup placer, - int stripWidth) { + public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView, + final ViewGroup placer, final int stripWidth) { if (suggestedWords.mIsPunctuationSuggestions) { layoutPunctuationSuggestions(suggestedWords, stripView); return; @@ -402,7 +409,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick x += word.getMeasuredWidth(); if (DBG && pos < suggestedWords.size()) { - final CharSequence debugInfo = Utils.getDebugInfo(suggestedWords, pos); + final String debugInfo = Utils.getDebugInfo(suggestedWords, pos); if (debugInfo != null) { final TextView info = mInfos.get(pos); info.setText(debugInfo); @@ -418,14 +425,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - private int getSuggestionWidth(int index, int maxWidth) { + private int getSuggestionWidth(final int index, final int maxWidth) { final int paddings = mPadding * mSuggestionsCountInStrip; final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1); final int availableWidth = maxWidth - paddings - dividers; return (int)(availableWidth * getSuggestionWeight(index)); } - private float getSuggestionWeight(int index) { + private float getSuggestionWeight(final int index) { if (index == mCenterSuggestionIndex) { return mCenterSuggestionWeight; } else { @@ -434,7 +441,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - private void setupTexts(SuggestedWords suggestedWords, int countInStrip) { + private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) { mTexts.clear(); final int count = Math.min(suggestedWords.size(), countInStrip); for (int pos = 0; pos < count; pos++) { @@ -447,8 +454,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - private void layoutPunctuationSuggestions(SuggestedWords suggestedWords, - ViewGroup stripView) { + private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords, + final ViewGroup stripView) { final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP); for (int index = 0; index < countInStrip; index++) { if (index != 0) { @@ -459,7 +466,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick final TextView word = mWords.get(index); word.setEnabled(true); word.setTextColor(mColorAutoCorrect); - final CharSequence text = suggestedWords.getWord(index); + final String text = suggestedWords.getWord(index); word.setText(text); word.setTextScaleX(1.0f); word.setCompoundDrawables(null, null, null, null); @@ -469,8 +476,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mMoreSuggestionsAvailable = false; } - public void layoutAddToDictionaryHint(CharSequence word, ViewGroup stripView, - int stripWidth, CharSequence hintText, OnClickListener listener) { + public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView, + final int stripWidth, final CharSequence hintText, final OnClickListener listener) { final int width = stripWidth - mDividerWidth - mPadding * 2; final TextView wordView = mWordToSaveView; @@ -511,11 +518,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return (CharSequence)mWordToSaveView.getTag(); } - public boolean isAddToDictionaryShowing(View v) { + public boolean isAddToDictionaryShowing(final View v) { return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView; } - private static void setLayoutWeight(View v, float weight, int height) { + private static void setLayoutWeight(final View v, final float weight, final int height) { final ViewGroup.LayoutParams lp = v.getLayoutParams(); if (lp instanceof LinearLayout.LayoutParams) { final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; @@ -525,7 +532,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) { + private static float getTextScaleX(final CharSequence text, final int maxWidth, + final TextPaint paint) { paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); if (width <= maxWidth) { @@ -534,8 +542,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return maxWidth / (float)width; } - private static CharSequence getEllipsizedText(CharSequence text, int maxWidth, - TextPaint paint) { + private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth, + final TextPaint paint) { if (text == null) return null; paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); @@ -556,7 +564,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return ellipsized; } - private static int getTextWidth(CharSequence text, TextPaint paint) { + private static int getTextWidth(final CharSequence text, final TextPaint paint) { if (TextUtils.isEmpty(text)) return 0; final Typeface savedTypeface = paint.getTypeface(); paint.setTypeface(getTextTypeface(text)); @@ -571,7 +579,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return width; } - private static Typeface getTextTypeface(CharSequence text) { + private static Typeface getTextTypeface(final CharSequence text) { if (!(text instanceof SpannableString)) return Typeface.DEFAULT; @@ -593,11 +601,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick * @param context * @param attrs */ - public SuggestionStripView(Context context, AttributeSet attrs) { + public SuggestionStripView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.suggestionStripViewStyle); } - public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) { + public SuggestionStripView(final Context context, final AttributeSet attrs, + final int defStyle) { super(context, attrs, defStyle); final LayoutInflater inflater = LayoutInflater.from(context); @@ -658,15 +667,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick * A connection back to the input method. * @param listener */ - public void setListener(Listener listener, View inputView) { + public void setListener(final Listener listener, final View inputView) { mListener = listener; mKeyboardView = (KeyboardView)inputView.findViewById(R.id.keyboard_view); } - public void setSuggestions(SuggestedWords suggestedWords) { - if (suggestedWords == null) - return; - + public void setSuggestions(final SuggestedWords suggestedWords) { clear(); mSuggestedWords = suggestedWords; mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth()); @@ -675,7 +681,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - public int setMoreSuggestionsHeight(int remainingHeight) { + public int setMoreSuggestionsHeight(final int remainingHeight) { return mParams.setMoreSuggestionsHeight(remainingHeight); } @@ -684,7 +690,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0)); } - public void showAddToDictionaryHint(CharSequence word, CharSequence hintText) { + public void showAddToDictionaryHint(final String word, final CharSequence hintText) { clear(); mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this); } @@ -708,16 +714,16 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick dismissMoreSuggestions(); } - private void hidePreview() { + void hidePreview() { mPreviewPopup.dismiss(); } private final KeyboardActionListener mMoreSuggestionsListener = new KeyboardActionListener.Adapter() { @Override - public boolean onCustomRequest(int requestCode) { + public boolean onCustomRequest(final int requestCode) { final int index = requestCode; - final CharSequence word = mSuggestedWords.getWord(index); + final String word = mSuggestedWords.getWord(index); mListener.pickSuggestionManually(index, word); dismissMoreSuggestions(); return true; @@ -737,7 +743,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } }; - private boolean dismissMoreSuggestions() { + boolean dismissMoreSuggestions() { if (mMoreSuggestionsWindow.isShowing()) { mMoreSuggestionsWindow.dismiss(); return true; @@ -746,41 +752,43 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } @Override - public boolean onLongClick(View view) { + public boolean onLongClick(final View view) { + KeyboardSwitcher.getInstance().hapticAndAudioFeedback(Constants.NOT_A_CODE); return showMoreSuggestions(); } - private boolean showMoreSuggestions() { + boolean showMoreSuggestions() { + final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard(); + if (parentKeyboard == null) { + return false; + } final SuggestionStripViewParams params = mParams; - if (params.mMoreSuggestionsAvailable) { - final int stripWidth = getWidth(); - final View container = mMoreSuggestionsContainer; - final int maxWidth = stripWidth - container.getPaddingLeft() - - container.getPaddingRight(); - final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; - builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth, - (int)(maxWidth * params.mMinMoreSuggestionsWidth), - params.getMaxMoreSuggestionsRow()); - mMoreSuggestionsView.setKeyboard(builder.build()); - container.measure( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; - final int pointX = stripWidth / 2; - final int pointY = -params.mMoreSuggestionsBottomGap; - moreKeysPanel.showMoreKeysPanel( - this, mMoreSuggestionsController, pointX, pointY, - mMoreSuggestionsWindow, mMoreSuggestionsListener); - mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING; - mOriginX = mLastX; - mOriginY = mLastY; - mKeyboardView.dimEntireKeyboard(true); - for (int i = 0; i < params.mSuggestionsCountInStrip; i++) { - mWords.get(i).setPressed(false); - } - return true; + if (!params.mMoreSuggestionsAvailable) { + return false; } - return false; + final int stripWidth = getWidth(); + final View container = mMoreSuggestionsContainer; + final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); + final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; + builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth, + (int)(maxWidth * params.mMinMoreSuggestionsWidth), + params.getMaxMoreSuggestionsRow(), parentKeyboard); + mMoreSuggestionsView.setKeyboard(builder.build()); + container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; + final int pointX = stripWidth / 2; + final int pointY = -params.mMoreSuggestionsBottomGap; + moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY, + mMoreSuggestionsWindow, mMoreSuggestionsListener); + mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING; + mOriginX = mLastX; + mOriginY = mLastY; + mKeyboardView.dimEntireKeyboard(true); + for (int i = 0; i < params.mSuggestionsCountInStrip; i++) { + mWords.get(i).setPressed(false); + } + return true; } // Working variables for onLongClick and dispatchTouchEvent. @@ -807,7 +815,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick }; @Override - public boolean dispatchTouchEvent(MotionEvent me) { + public boolean dispatchTouchEvent(final MotionEvent me) { if (!mMoreSuggestionsWindow.isShowing() || mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) { mLastX = (int)me.getX(); @@ -849,7 +857,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } @Override - public void onClick(View view) { + public void onClick(final View view) { if (mParams.isAddToDictionaryShowing(view)) { mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString()); clear(); @@ -863,7 +871,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (index >= mSuggestedWords.size()) return; - final CharSequence word = mSuggestedWords.getWord(index); + final String word = mSuggestedWords.getWord(index); mListener.pickSuggestionManually(index, word); } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 763fd6e00..6295abe8c 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -870,7 +870,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final long time = SystemClock.uptimeMillis(); final ResearchLogger researchLogger = getInstance(); final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y + Constants.printableCode(scrubDigitFromCodePoint(code)), x, y }; researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); if (Character.isDigit(code)) { @@ -1006,7 +1006,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }; public static void latinIME_sendKeyCodePoint(final int code) { final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)) + Constants.printableCode(scrubDigitFromCodePoint(code)) }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); @@ -1092,7 +1092,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (key != null) { String outputText = key.getOutputText(); final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null + Constants.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null : scrubDigitsFromString(outputText.toString()), x, y, ignoreModifierKey, altersCode, key.isEnabled() }; @@ -1109,7 +1109,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final boolean withSliding, final boolean ignoreModifierKey) { if (key != null) { final Object[] values = { - Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding, + Constants.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding, ignoreModifierKey, key.isEnabled() }; getInstance().enqueuePotentiallyPrivateEvent( diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index eab465aa2..98491bd23 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -16,7 +16,7 @@ package com.android.inputmethod.research; -import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.Constants; public class Statistics { // Number of characters entered during a typing session @@ -104,7 +104,7 @@ public class Statistics { public void recordChar(int codePoint, long time) { final long delta = time - mLastTapTime; - if (codePoint == Keyboard.CODE_DELETE) { + if (codePoint == Constants.CODE_DELETE) { mDeleteKeyCount++; if (delta < MIN_DELETION_INTERMISSION) { if (mIsLastKeyDeleteKey) { |