diff options
Diffstat (limited to 'java')
80 files changed, 2700 insertions, 2180 deletions
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml index 2e0cddc28..b9451f8ae 100644 --- a/java/res/layout/input_view.xml +++ b/java/res/layout/input_view.xml @@ -43,7 +43,7 @@ android:layout_width="@dimen/suggestions_strip_padding" android:layout_height="@dimen/suggestions_strip_height" style="?attr/suggestionsStripBackgroundStyle" /> - <com.android.inputmethod.latin.SuggestionsView + <com.android.inputmethod.latin.suggestions.SuggestionsView android:id="@+id/suggestions_view" android:layout_weight="1.0" android:layout_width="0dp" diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml index 6aa82e197..1e59b8931 100644 --- a/java/res/layout/more_suggestions.xml +++ b/java/res/layout/more_suggestions.xml @@ -24,7 +24,7 @@ android:orientation="horizontal" style="?attr/miniKeyboardPanelStyle" > - <com.android.inputmethod.latin.MoreSuggestionsView + <com.android.inputmethod.latin.suggestions.MoreSuggestionsView xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" android:id="@+id/more_suggestions_view" android:layout_alignParentBottom="true" diff --git a/java/res/values-be/donottranslate-more-keys.xml b/java/res/values-be/donottranslate-more-keys.xml new file mode 100644 index 000000000..28264c4ac --- /dev/null +++ b/java/res/values-be/donottranslate-more-keys.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="keylabel_for_slavic_shcha">ў</string> + <string name="keylabel_for_slavic_i">i</string> +</resources> diff --git a/java/res/values-ky/donottranslate-more-keys.xml b/java/res/values-ky/donottranslate-more-keys.xml new file mode 100644 index 000000000..d56cde577 --- /dev/null +++ b/java/res/values-ky/donottranslate-more-keys.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="more_keys_for_slavic_u">3,ү</string> + <string name="more_keys_for_slavic_en">6,ң</string> + <string name="more_keys_for_slavic_o">ө</string> +</resources> diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml index 4a5c5a44c..dcbfe463e 100644 --- a/java/res/values-land/dimens.xml +++ b/java/res/values-land/dimens.xml @@ -53,6 +53,7 @@ <fraction name="key_hint_label_ratio">52%</fraction> <fraction name="key_uppercase_letter_ratio">40%</fraction> <fraction name="key_preview_text_ratio">90%</fraction> + <fraction name="spacebar_text_ratio">40.000%</fraction> <dimen name="key_preview_offset">0.08in</dimen> <dimen name="key_preview_offset_ics">0.01in</dimen> diff --git a/java/res/values-ro/donottranslate-more-keys.xml b/java/res/values-ro/donottranslate-more-keys.xml index d7e6a171d..51df56099 100644 --- a/java/res/values-ro/donottranslate-more-keys.xml +++ b/java/res/values-ro/donottranslate-more-keys.xml @@ -18,7 +18,7 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="more_keys_for_a">ă,â,à,á,ä,æ,ã,å,ā</string> + <string name="more_keys_for_a">â,ã,ă,à,á,ä,æ,å,ā</string> <string name="more_keys_for_i">8,î,ï,ì,í,į,ī</string> <string name="more_keys_for_s">ș,ß,ś,š</string> <string name="more_keys_for_t">5,ț</string> diff --git a/java/res/values-ru/donottranslate-more-keys.xml b/java/res/values-ru/donottranslate-more-keys.xml index f7e006e84..7ae9ffbda 100644 --- a/java/res/values-ru/donottranslate-more-keys.xml +++ b/java/res/values-ru/donottranslate-more-keys.xml @@ -18,7 +18,5 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="more_keys_for_cyrillic_e">5,ё</string> - <string name="more_keys_for_cyrillic_soft_sign">ъ</string> - <string name="more_keys_for_cyrillic_ha">ъ</string> + <string name="more_keys_for_slavic_ye">5,ё</string> </resources> diff --git a/java/res/values-sk/donottranslate-more-keys.xml b/java/res/values-sk/donottranslate-more-keys.xml index b73db0a46..b6b35c1a3 100644 --- a/java/res/values-sk/donottranslate-more-keys.xml +++ b/java/res/values-sk/donottranslate-more-keys.xml @@ -18,18 +18,20 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="more_keys_for_a">ä,á,à,â,æ,ã,å,ā</string> - <string name="more_keys_for_e">3,é,ě,è,ê,ë,ę,ė,ē</string> - <string name="more_keys_for_i">8,í,î,ï,ì,į,ī</string> - <string name="more_keys_for_o">9,ô,ó,ö,ò,õ,œ,ø,ō</string> - <string name="more_keys_for_u">7,ú,ú,û,ü,ù,ū</string> - <string name="more_keys_for_s">š,ß,ś</string> - <string name="more_keys_for_n">ň,ñ,ń</string> + <string name="more_keys_for_a">á,ä,ā,à,â,ã,å,æ,ą</string> + <string name="more_keys_for_e">3,é,ě,ē,ė,è,ê,ë,ę</string> + <string name="more_keys_for_i">8,í,ī,į,ì,î,ï,ı</string> + <string name="more_keys_for_o">9,ô,ó,ö,ò,õ,œ,ő,ø</string> + <string name="more_keys_for_u">7,ú,ů,ü,ū,ų,ù,û,ű</string> + <string name="more_keys_for_s">š,ß,ś,ş</string> + <string name="more_keys_for_n">ň,ņ,ñ,ń,ń</string> <string name="more_keys_for_c">č,ç,ć</string> <string name="more_keys_for_y">6,ý,ÿ</string> <string name="more_keys_for_d">ď</string> - <string name="more_keys_for_r">4,ŕ,ř</string> - <string name="more_keys_for_t">5,ť</string> - <string name="more_keys_for_z">ž,ź,ż</string> - <string name="more_keys_for_l">ľ,ĺ,ł</string> + <string name="more_keys_for_r">4,ŕ,ř,ŗ</string> + <string name="more_keys_for_t">5,ť,ţ</string> + <string name="more_keys_for_z">ž,ż,ź</string> + <string name="more_keys_for_k">ķ</string> + <string name="more_keys_for_l">ľ,ĺ,ļ,ł</string> + <string name="more_keys_for_g">ģ,ğ</string> </resources> diff --git a/java/res/values-sl/donottranslate-more-keys.xml b/java/res/values-sl/donottranslate-more-keys.xml new file mode 100644 index 000000000..b72c6799e --- /dev/null +++ b/java/res/values-sl/donottranslate-more-keys.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="more_keys_for_s">š</string> + <string name="more_keys_for_c">č,ć</string> + <string name="more_keys_for_d">đ</string> + <string name="more_keys_for_z">ž</string> +</resources> diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml index 1b8c8a64a..1c725a484 100644 --- a/java/res/values-sw600dp-land/dimens.xml +++ b/java/res/values-sw600dp-land/dimens.xml @@ -48,6 +48,7 @@ <fraction name="key_hint_letter_ratio">23%</fraction> <fraction name="key_hint_label_ratio">34%</fraction> <fraction name="key_uppercase_letter_ratio">29%</fraction> + <fraction name="spacebar_text_ratio">33.33%</fraction> <dimen name="suggestions_strip_padding">40.0mm</dimen> <integer name="max_more_suggestions_row">5</integer> diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml index 1854a8696..1dd93121d 100644 --- a/java/res/values-sw600dp/config.xml +++ b/java/res/values-sw600dp/config.xml @@ -38,6 +38,5 @@ <integer name="config_long_press_space_key_timeout">0</integer> <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. --> <string name="config_default_keyboard_theme_id" translatable="false">5</string> - <string name="config_text_size_of_language_on_spacebar" translatable="false">medium</string> <integer name="config_max_more_keys_column">5</integer> </resources> diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml index 31830239d..e393be579 100644 --- a/java/res/values-sw600dp/dimens.xml +++ b/java/res/values-sw600dp/dimens.xml @@ -59,6 +59,7 @@ <fraction name="key_hint_label_ratio">28%</fraction> <fraction name="key_uppercase_letter_ratio">26%</fraction> <fraction name="key_preview_text_ratio">50%</fraction> + <fraction name="spacebar_text_ratio">32.14%</fraction> <dimen name="key_preview_height">15.0mm</dimen> <dimen name="key_preview_offset">0.1in</dimen> diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml index 664e8c159..ef39f4393 100644 --- a/java/res/values-sw768dp-land/dimens.xml +++ b/java/res/values-sw768dp-land/dimens.xml @@ -52,6 +52,7 @@ <fraction name="key_hint_letter_ratio">23%</fraction> <fraction name="key_hint_label_ratio">28%</fraction> <fraction name="key_uppercase_letter_ratio">24%</fraction> + <fraction name="spacebar_text_ratio">24.00%</fraction> <dimen name="key_preview_height">17.0mm</dimen> <dimen name="key_preview_height_ics">26.5mm</dimen> diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml index c25139a42..06553a7c9 100644 --- a/java/res/values-sw768dp/config.xml +++ b/java/res/values-sw768dp/config.xml @@ -36,7 +36,6 @@ <integer name="config_long_press_space_key_timeout">0</integer> <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. --> <string name="config_default_keyboard_theme_id" translatable="false">5</string> - <string name="config_text_size_of_language_on_spacebar" translatable="false">medium</string> <integer name="config_max_more_keys_column">5</integer> <!-- Screen metrics for logging. 0 = "mdpi phone screen" diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml index bb4937dd5..dbbd844f0 100644 --- a/java/res/values-sw768dp/dimens.xml +++ b/java/res/values-sw768dp/dimens.xml @@ -62,6 +62,7 @@ <fraction name="key_hint_label_ratio">28%</fraction> <fraction name="key_uppercase_letter_ratio">26%</fraction> <fraction name="key_preview_text_ratio">50%</fraction> + <fraction name="spacebar_text_ratio">29.03%</fraction> <dimen name="key_preview_height">15.0mm</dimen> <dimen name="key_preview_offset">0.1in</dimen> diff --git a/java/res/values-uk/donottranslate-more-keys.xml b/java/res/values-uk/donottranslate-more-keys.xml new file mode 100644 index 000000000..4e7910128 --- /dev/null +++ b/java/res/values-uk/donottranslate-more-keys.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="keylabel_for_slavic_yery">і</string> + <string name="more_keys_for_slavic_yery">ї</string> +</resources> diff --git a/java/res/values-vi/donottranslate-more-keys.xml b/java/res/values-vi/donottranslate-more-keys.xml new file mode 100644 index 000000000..97a7d79ae --- /dev/null +++ b/java/res/values-vi/donottranslate-more-keys.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="more_keys_for_a">à,á,ả,ã,ạ,ă,ằ,ắ,ẳ,ẵ,ặ,â,ầ,ấ,ẩ,ẫ,ậ</string> + <string name="more_keys_for_e">3,è,é,ẻ,ẽ,ẹ,ê,ề,ế,ể,ễ,ệ</string> + <string name="more_keys_for_i">8,ì,í,ỉ,ĩ,ị</string> + <string name="more_keys_for_o">9,ò,ó,ỏ,õ,ọ,ô,ồ,ố,ổ,ỗ,ộ,ơ,ờ,ớ,ở,ỡ,ợ</string> + <string name="more_keys_for_u">7,ù,ú,ủ,ũ,ụ,ư,ừ,ứ,ử,ữ,ự</string> + <string name="more_keys_for_y">6,ỳ,ý,ỷ,ỹ,ỵ</string> + <string name="more_keys_for_d">đ</string> +</resources> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 94f7ab78b..9892d7835 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -18,10 +18,13 @@ <declare-styleable name="KeyboardTheme"> <!-- Keyboard style --> <attr name="keyboardStyle" format="reference" /> + <!-- TODO: Get rid of latinKeyboardStyle --> <!-- LatinKeyboard style --> <attr name="latinKeyboardStyle" format="reference" /> <!-- KeyboardView style --> <attr name="keyboardViewStyle" format="reference" /> + <!-- LatinKeyboardView style --> + <attr name="latinKeyboardViewStyle" format="reference" /> <!-- MiniKeyboard style --> <attr name="miniKeyboardStyle" format="reference" /> <!-- MiniKeyboardView style --> @@ -120,6 +123,15 @@ </attr> </declare-styleable> + <declare-styleable name="LatinKeyboardView"> + <attr name="autoCorrectionSpacebarLedEnabled" format="boolean" /> + <attr name="autoCorrectionSpacebarLedIcon" format="reference" /> + <!-- Size of the text for spacebar language label, in the proportion of key height. --> + <attr name="spacebarTextRatio" format="fraction" /> + <attr name="spacebarTextColor" format="color" /> + <attr name="spacebarTextShadowColor" format="color" /> + </declare-styleable> + <declare-styleable name="SuggestionsView"> <attr name="suggestionStripOption" format="integer"> <!-- This should be aligned with SuggestionsViewParams.AUTO_CORRECT_* and etc. --> @@ -127,9 +139,11 @@ <flag name="autoCorrectUnderline" value="0x02" /> <flag name="validTypedWordBold" value="0x04" /> </attr> + <attr name="colorValidTypedWord" format="color" /> <attr name="colorTypedWord" format="color" /> <attr name="colorAutoCorrect" format="color" /> <attr name="colorSuggested" format="color" /> + <attr name="alphaValidTypedWord" format="integer" /> <attr name="alphaTypedWord" format="integer" /> <attr name="alphaAutoCorrect" format="integer" /> <attr name="alphaSuggested" format="integer" /> @@ -176,7 +190,9 @@ <attr name="iconTabKey" format="reference" /> <attr name="iconShortcutKey" format="reference" /> <attr name="iconShortcutForLabel" format="reference" /> - <attr name="iconShiftedShiftKey" format="reference" /> + <attr name="iconSpaceKeyForNumberLayout" format="reference" /> + <attr name="iconShiftKeyShifted" format="reference" /> + <attr name="iconDisabledShortcutKey" format="reference" /> <attr name="iconPreviewTabKey" format="reference" /> </declare-styleable> @@ -229,26 +245,30 @@ </attr> <!-- The icon to display on the key instead of the label. --> <attr name="keyIcon" format="enum"> - <!-- This should be aligned with KeyboardIcons.ICON_* --> + <!-- This should be aligned with the KeyboardIcons.ICONS_TO_ATTRS_MAP --> <enum name="iconShiftKey" value="1" /> <enum name="iconDeleteKey" value="2" /> + <!-- This is also represented as "@icon/3" in keyboard layout XML. --> <enum name="iconSettingsKey" value="3" /> <enum name="iconSpaceKey" value="4" /> <enum name="iconReturnKey" value="5" /> <enum name="iconSearchKey" value="6" /> + <!-- This is also represented as "@icon/7" in keyboard layout XML. --> <enum name="iconTabKey" value="7" /> <enum name="iconShortcutKey" value="8" /> <enum name="iconShortcutForLabel" value="9" /> + <enum name="iconSpaceKeyForNumberLayout" value="10" /> + <enum name="iconShiftKeyShifted" value="11" /> </attr> - <!-- Shift key icon for shifted state --> - <attr name="keyIconShifted" format="enum"> - <!-- This should be aligned with KeyboardIcons.ICON_SHIFTED_* --> - <enum name="iconShiftedShiftKey" value="10" /> + <!-- The icon for disabled key --> + <attr name="keyIconDisabled" format="enum"> + <!-- This should be aligned with the KeyboardIcons.ICONS_TO_ATTRS_MAP --> + <enum name="iconDisabledShortcutKey" value="12" /> </attr> <!-- The icon to show in the popup preview. --> <attr name="keyIconPreview" format="enum"> - <!-- This should be aligned with KeyboardIcons.ICON_PREVIEW_* --> - <enum name="iconPreviewTabKey" value="11" /> + <!-- This should be aligned with the KeyboardIcons.ICONS_TO_ATTRS_MAP --> + <enum name="iconPreviewTabKey" value="13" /> </attr> <!-- The key style to specify a set of key attributes defined by <key_style/> --> <attr name="keyStyle" format="string" /> @@ -319,14 +339,6 @@ <attr name="parentStyle" format="string" /> </declare-styleable> - <declare-styleable name="LatinKeyboard"> - <attr name="autoCorrectionSpacebarLedEnabled" format="boolean" /> - <attr name="autoCorrectionSpacebarLedIcon" format="reference" /> - <attr name="disabledShortcutIcon" format="reference" /> - <attr name="spacebarTextColor" format="color" /> - <attr name="spacebarTextShadowColor" format="color" /> - </declare-styleable> - <declare-styleable name="KeyboardSet"> <!-- Locale of the keyboard layouts --> <attr name="keyboardLocale" format="string" /> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 8b99a1fcb..55f35f0c5 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -66,7 +66,6 @@ <dimen name="config_touch_noise_threshold_distance">2.0mm</dimen> <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. --> <string name="config_default_keyboard_theme_id" translatable="false">5</string> - <string name="config_text_size_of_language_on_spacebar" translatable="false">small</string> <integer name="config_max_more_keys_column">5</integer> <string-array name="auto_correction_threshold_values" translatable="false"> <!-- Off, When auto correction setting is Off, this value is not used. --> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index 352141ca6..e46ff7718 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -66,6 +66,7 @@ <fraction name="key_hint_label_ratio">44%</fraction> <fraction name="key_uppercase_letter_ratio">35%</fraction> <fraction name="key_preview_text_ratio">82%</fraction> + <fraction name="spacebar_text_ratio">33.735%</fraction> <dimen name="key_preview_height">80sp</dimen> <dimen name="key_preview_offset">0.1in</dimen> diff --git a/java/res/values/donottranslate-more-keys.xml b/java/res/values/donottranslate-more-keys.xml index 6c7753999..ac175dfe7 100644 --- a/java/res/values/donottranslate-more-keys.xml +++ b/java/res/values/donottranslate-more-keys.xml @@ -42,9 +42,16 @@ <string name="keylabel_for_scandinavia_row2_11"></string> <string name="more_keys_for_scandinavia_row2_10"></string> <string name="more_keys_for_scandinavia_row2_11"></string> - <string name="more_keys_for_cyrillic_e"></string> - <string name="more_keys_for_cyrillic_soft_sign"></string> - <string name="more_keys_for_cyrillic_ha"></string> + <string name="keylabel_for_slavic_shcha">щ</string> + <string name="keylabel_for_slavic_yery">ы</string> + <string name="keylabel_for_slavic_i">и</string> + <string name="more_keys_for_slavic_u">3</string> + <string name="more_keys_for_slavic_ye">5</string> + <string name="more_keys_for_slavic_en">6</string> + <string name="more_keys_for_slavic_ha">ъ</string> + <string name="more_keys_for_slavic_yery"></string> + <string name="more_keys_for_slavic_o"></string> + <string name="more_keys_for_slavic_soft_sign">ъ</string> <string name="more_keys_for_currency_dollar">¢,£,€,¥,₱</string> <string name="more_keys_for_currency_euro">¢,£,$,¥,₱</string> <string name="more_keys_for_currency_pound">¢,$,€,¥,₱</string> diff --git a/java/res/values/keyboard-icons-black.xml b/java/res/values/keyboard-icons-black.xml index f767cb349..1c5a5f720 100644 --- a/java/res/values/keyboard-icons-black.xml +++ b/java/res/values/keyboard-icons-black.xml @@ -30,10 +30,9 @@ <item name="iconTabKey">@drawable/sym_bkeyboard_tab</item> <item name="iconShortcutKey">@drawable/sym_bkeyboard_mic</item> <item name="iconShortcutForLabel">@drawable/sym_bkeyboard_label_mic</item> - <item name="iconShiftedShiftKey">@drawable/sym_bkeyboard_shift_locked</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_bkeyboard_space</item> + <item name="iconShiftKeyShifted">@drawable/sym_bkeyboard_shift_locked</item> + <item name="iconDisabledShortcutKey">@drawable/sym_bkeyboard_voice_off</item> <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item> - <!-- LatinKeyboard icons --> - <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> - <item name="disabledShortcutIcon">@drawable/sym_bkeyboard_voice_off</item> </style> </resources> diff --git a/java/res/values/keyboard-icons-ics.xml b/java/res/values/keyboard-icons-ics.xml index f1021433d..f68be5f1e 100644 --- a/java/res/values/keyboard-icons-ics.xml +++ b/java/res/values/keyboard-icons-ics.xml @@ -23,16 +23,15 @@ <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo</item> <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo</item> <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo</item> - <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo</item> + <item name="iconSpaceKey">@null</item> <item name="iconReturnKey">@drawable/sym_keyboard_return_holo</item> <item name="iconSearchKey">@drawable/sym_keyboard_search_holo</item> <item name="iconTabKey">@drawable/sym_keyboard_tab_holo</item> <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item> <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo</item> - <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked_holo</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo</item> + <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo</item> + <item name="iconDisabledShortcutKey">@drawable/sym_keyboard_voice_off_holo</item> <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item> - <!-- LatinKeyboard icons --> - <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> - <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item> </style> </resources> diff --git a/java/res/values/keyboard-icons-white.xml b/java/res/values/keyboard-icons-white.xml index 07ece66b1..35197a1c0 100644 --- a/java/res/values/keyboard-icons-white.xml +++ b/java/res/values/keyboard-icons-white.xml @@ -26,10 +26,10 @@ <item name="iconTabKey">@drawable/sym_keyboard_tab</item> <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item> <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic</item> - <item name="iconShiftedShiftKey">@drawable/sym_keyboard_shift_locked</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space</item> + <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked</item> + <!-- TODO: Needs non-holo disabled shortcut icon drawable --> + <item name="iconDisabledShortcutKey">@drawable/sym_keyboard_voice_off_holo</item> <item name="iconPreviewTabKey">@drawable/sym_keyboard_feedback_tab</item> - <!-- LatinKeyboard icons --> - <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> - <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item> </style> </resources> diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index 2b5fb08e5..c4e39e357 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -31,11 +31,6 @@ <item name="verticalGap">@fraction/key_bottom_gap</item> <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item> </style> - <style name="LatinKeyboard"> - <item name="autoCorrectionSpacebarLedEnabled">true</item> - <item name="spacebarTextColor">#FFC0C0C0</item> - <item name="spacebarTextShadowColor">#80000000</item> - </style> <style name="KeyboardView"> <item name="android:background">@drawable/keyboard_background</item> <item name="keyBackground">@drawable/btn_keyboard_key</item> @@ -71,6 +66,15 @@ <item name="backgroundDimAmount">0.5</item> </style> <style + name="LatinKeyboardView" + parent="KeyboardView"> + <item name="autoCorrectionSpacebarLedEnabled">true</item> + <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> + <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="spacebarTextColor">#FFC0C0C0</item> + <item name="spacebarTextShadowColor">#80000000</item> + </style> + <style name="MiniKeyboard" parent="Keyboard" > @@ -97,7 +101,8 @@ name="SuggestionsViewStyle" parent="SuggestionsStripBackgroundStyle" > - <item name="suggestionStripOption">autoCorrectBold</item> + <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item> + <item name="colorValidTypedWord">#FFFCAE00</item> <item name="colorTypedWord">@android:color/white</item> <item name="colorAutoCorrect">#FFFCAE00</item> <item name="colorSuggested">#FFFCAE00</item> @@ -132,6 +137,16 @@ <item name="android:background">@android:color/black</item> <item name="keyBackground">@drawable/btn_keyboard_key3</item> </style> + <style + name="LatinKeyboardView.HighContrast" + parent="KeyboardView.HighContrast" + > + <item name="autoCorrectionSpacebarLedEnabled">true</item> + <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> + <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="spacebarTextColor">#FFC0C0C0</item> + <item name="spacebarTextShadowColor">#80000000</item> + </style> <!-- Theme "Stone" --> <style name="Keyboard.Stone" @@ -145,13 +160,6 @@ <item name="verticalGap">@fraction/key_bottom_gap_stone</item> </style> <style - name="LatinKeyboard.Stone" - parent="LatinKeyboard" - > - <item name="spacebarTextColor">#FF000000</item> - <item name="spacebarTextShadowColor">#D0FFFFFF</item> - </style> - <style name="KeyboardView.Stone" parent="KeyboardView" > @@ -165,6 +173,16 @@ <item name="shadowColor">#FFFFFFFF</item> </style> <style + name="LatinKeyboardView.Stone" + parent="KeyboardView.Stone" + > + <item name="autoCorrectionSpacebarLedEnabled">true</item> + <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> + <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="spacebarTextColor">#FF000000</item> + <item name="spacebarTextShadowColor">#D0FFFFFF</item> + </style> + <style name="MiniKeyboard.Stone" parent="Keyboard.Stone" > @@ -193,6 +211,16 @@ > <item name="keyTextStyle">bold</item> </style> + <style + name="LatinKeyboardView.Stone.Bold" + parent="KeyboardView.Stone.Bold" + > + <item name="autoCorrectionSpacebarLedEnabled">true</item> + <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> + <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="spacebarTextColor">#FF000000</item> + <item name="spacebarTextShadowColor">#D0FFFFFF</item> + </style> <!-- Theme "Gingerbread" --> <style name="Keyboard.Gingerbread" @@ -212,6 +240,16 @@ <item name="keyTextStyle">bold</item> </style> <style + name="LatinKeyboardView.Gingerbread" + parent="KeyboardView.Gingerbread" + > + <item name="autoCorrectionSpacebarLedEnabled">true</item> + <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item> + <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="spacebarTextColor">#FFC0C0C0</item> + <item name="spacebarTextShadowColor">#80000000</item> + </style> + <style name="MiniKeyboard.Gingerbread" parent="Keyboard.Gingerbread" > @@ -238,13 +276,6 @@ <item name="touchPositionCorrectionData">@array/touch_position_correction_data_ice_cream_sandwich</item> </style> <style - name="LatinKeyboard.IceCreamSandwich" - parent="LatinKeyboard" - > - <item name="autoCorrectionSpacebarLedEnabled">false</item> - <item name="disabledShortcutIcon">@drawable/sym_keyboard_voice_off_holo</item> - </style> - <style name="KeyboardView.IceCreamSandwich" parent="KeyboardView" > @@ -268,6 +299,16 @@ <item name="shadowRadius">0.0</item> </style> <style + name="LatinKeyboardView.IceCreamSandwich" + parent="KeyboardView.IceCreamSandwich" + > + <item name="autoCorrectionSpacebarLedEnabled">false</item> + <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> + <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="spacebarTextColor">#FFC0C0C0</item> + <item name="spacebarTextShadowColor">#80000000</item> + </style> + <style name="MiniKeyboard.IceCreamSandwich" parent="Keyboard.IceCreamSandwich" > @@ -296,11 +337,12 @@ > <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item> <!-- android:color/holo_blue_light=#FF33B5E5 --> + <item name="colorValidTypedWord">@android:color/holo_blue_light</item> <item name="colorTypedWord">@android:color/holo_blue_light</item> <item name="colorAutoCorrect">@android:color/holo_blue_light</item> <item name="colorSuggested">@android:color/holo_blue_light</item> + <item name="alphaValidTypedWord">85</item> <item name="alphaTypedWord">85</item> - <item name="alphaAutoCorrect">100</item> <item name="alphaSuggested">70</item> <item name="alphaObsoleted">70</item> <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item> diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml index abb7c8057..0062b2837 100644 --- a/java/res/values/themes-basic-highcontrast.xml +++ b/java/res/values/themes-basic-highcontrast.xml @@ -17,8 +17,8 @@ <resources> <style name="KeyboardTheme.HighContrast" parent="KeyboardIcons"> <item name="keyboardStyle">@style/Keyboard.HighContrast</item> - <item name="latinKeyboardStyle">@style/LatinKeyboard</item> <item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item> + <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.HighContrast</item> <item name="miniKeyboardStyle">@style/MiniKeyboard</item> <item name="miniKeyboardViewStyle">@style/MiniKeyboardView</item> <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item> diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml index ff9fed55f..0786e08fc 100644 --- a/java/res/values/themes-basic.xml +++ b/java/res/values/themes-basic.xml @@ -17,8 +17,8 @@ <resources> <style name="KeyboardTheme" parent="KeyboardIcons"> <item name="keyboardStyle">@style/Keyboard</item> - <item name="latinKeyboardStyle">@style/LatinKeyboard</item> <item name="keyboardViewStyle">@style/KeyboardView</item> + <item name="latinKeyboardViewStyle">@style/LatinKeyboardView</item> <item name="miniKeyboardStyle">@style/MiniKeyboard</item> <item name="miniKeyboardViewStyle">@style/MiniKeyboardView</item> <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item> diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml index be853eb0f..44338d821 100644 --- a/java/res/values/themes-gingerbread.xml +++ b/java/res/values/themes-gingerbread.xml @@ -17,8 +17,8 @@ <resources> <style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons"> <item name="keyboardStyle">@style/Keyboard.Gingerbread</item> - <item name="latinKeyboardStyle">@style/LatinKeyboard</item> <item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item> + <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Gingerbread</item> <item name="miniKeyboardStyle">@style/MiniKeyboard.Gingerbread</item> <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Gingerbread</item> <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item> diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index 618aaed79..dbc8f32e3 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -17,8 +17,8 @@ <resources> <style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich"> <item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item> - <item name="latinKeyboardStyle">@style/LatinKeyboard.IceCreamSandwich</item> <item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item> + <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.IceCreamSandwich</item> <item name="miniKeyboardStyle">@style/MiniKeyboard.IceCreamSandwich</item> <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.IceCreamSandwich</item> <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle.IceCreamSandwich</item> diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml index 532a2985e..60f130d59 100644 --- a/java/res/values/themes-stone-bold.xml +++ b/java/res/values/themes-stone-bold.xml @@ -17,8 +17,8 @@ <resources> <style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black"> <item name="keyboardStyle">@style/Keyboard.Stone.Bold</item> - <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item> <item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item> + <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Stone.Bold</item> <item name="miniKeyboardStyle">@style/MiniKeyboard.Stone</item> <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Stone</item> <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item> diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml index cb3edc58f..9aaca3a6c 100644 --- a/java/res/values/themes-stone.xml +++ b/java/res/values/themes-stone.xml @@ -17,8 +17,8 @@ <resources> <style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black"> <item name="keyboardStyle">@style/Keyboard.Stone</item> - <item name="latinKeyboardStyle">@style/LatinKeyboard.Stone</item> <item name="keyboardViewStyle">@style/KeyboardView.Stone</item> + <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Stone</item> <item name="miniKeyboardStyle">@style/MiniKeyboard.Stone</item> <item name="miniKeyboardViewStyle">@style/MiniKeyboardView.Stone</item> <item name="miniKeyboardPanelStyle">@style/MiniKeyboardPanelStyle</item> diff --git a/java/res/xml-be/keyboard_set.xml b/java/res/xml-be/keyboard_set.xml new file mode 100644 index 000000000..e5c6ba35a --- /dev/null +++ b/java/res/xml-be/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="be"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_slavic" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-ky/keyboard_set.xml b/java/res/xml-ky/keyboard_set.xml new file mode 100644 index 000000000..7cdd0a169 --- /dev/null +++ b/java/res/xml-ky/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="ky"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_slavic" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-ro/keyboard_set.xml b/java/res/xml-ro/keyboard_set.xml new file mode 100644 index 000000000..725cb52d2 --- /dev/null +++ b/java/res/xml-ro/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="ro"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_qwerty" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-ru/keyboard_set.xml b/java/res/xml-ru/keyboard_set.xml index eabee5dc3..0a158d9cc 100644 --- a/java/res/xml-ru/keyboard_set.xml +++ b/java/res/xml-ru/keyboard_set.xml @@ -23,7 +23,7 @@ latin:keyboardLocale="ru"> <Element latin:elementName="alphabet" - latin:elementKeyboard="@xml/kbd_russian" /> + latin:elementKeyboard="@xml/kbd_slavic" /> <Element latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> diff --git a/java/res/xml-sk/keyboard_set.xml b/java/res/xml-sk/keyboard_set.xml new file mode 100644 index 000000000..9df01dda6 --- /dev/null +++ b/java/res/xml-sk/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="sk"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_qwerty" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-sl/keyboard_set.xml b/java/res/xml-sl/keyboard_set.xml new file mode 100644 index 000000000..d2ec4c02d --- /dev/null +++ b/java/res/xml-sl/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="sl"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_qwerty" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-sw600dp/kbd_key_styles.xml b/java/res/xml-sw600dp/kbd_key_styles.xml index e0171b030..1dd0307c7 100644 --- a/java/res/xml-sw600dp/kbd_key_styles.xml +++ b/java/res/xml-sw600dp/kbd_key_styles.xml @@ -43,7 +43,6 @@ latin:styleName="shiftKeyStyle" latin:code="@integer/key_shift" latin:keyIcon="iconShiftKey" - latin:keyIconShifted="iconShiftedShiftKey" latin:keyActionFlags="noKeyPreview" latin:backgroundType="sticky" /> <key-style @@ -77,6 +76,7 @@ latin:styleName="shortcutKeyStyle" latin:code="@integer/key_shortcut" latin:keyIcon="iconShortcutKey" + latin:keyIconDisabled="iconDisabledShortcutKey" latin:keyActionFlags="noKeyPreview|altCodeWhileTyping" latin:altCode="@integer/key_dummy" latin:parentStyle="f2PopupStyle" /> diff --git a/java/res/xml-sw600dp/kbd_rows_russian.xml b/java/res/xml-sw600dp/kbd_rows_slavic.xml index 3395065ed..0a162058d 100644 --- a/java/res/xml-sw600dp/kbd_rows_russian.xml +++ b/java/res/xml-sw600dp/kbd_rows_slavic.xml @@ -31,20 +31,22 @@ <Key latin:keyLabel="ц" /> <Key - latin:keyLabel="у" /> + latin:keyLabel="у" + latin:moreKeys="@string/more_keys_for_slavic_u" /> <Key latin:keyLabel="к" /> <Key latin:keyLabel="е" - latin:moreKeys="@string/more_keys_for_cyrillic_e" /> + latin:moreKeys="@string/more_keys_for_slavic_ye" /> <Key - latin:keyLabel="н" /> + latin:keyLabel="н" + latin:moreKeys="@string/more_keys_for_slavic_en" /> <Key latin:keyLabel="г" /> <Key latin:keyLabel="ш" /> <Key - latin:keyLabel="щ" /> + latin:keyLabel="@string/keylabel_for_slavic_shcha" /> <Key latin:keyLabel="з" /> <Key @@ -63,7 +65,8 @@ latin:keyLabel="ф" latin:keyXPos="2.25%p" /> <Key - latin:keyLabel="ы" /> + latin:keyLabel="@string/keylabel_for_slavic_yery" + latin:moreKeys="@string/more_keys_for_slavic_yery" /> <Key latin:keyLabel="в" /> <Key @@ -73,7 +76,8 @@ <Key latin:keyLabel="р" /> <Key - latin:keyLabel="о" /> + latin:keyLabel="о" + latin:moreKeys="@string/more_keys_for_slavic_o" /> <Key latin:keyLabel="л" /> <Key @@ -101,7 +105,7 @@ <Key latin:keyLabel="м" /> <Key - latin:keyLabel="и" /> + latin:keyLabel="@string/keylabel_for_slavic_i" /> <Key latin:keyLabel="т" /> <Key diff --git a/java/res/xml-sw768dp/kbd_key_styles.xml b/java/res/xml-sw768dp/kbd_key_styles.xml index d9266508e..7292fe169 100644 --- a/java/res/xml-sw768dp/kbd_key_styles.xml +++ b/java/res/xml-sw768dp/kbd_key_styles.xml @@ -25,7 +25,6 @@ latin:styleName="shiftKeyStyle" latin:code="@integer/key_shift" latin:keyIcon="iconShiftKey" - latin:keyIconShifted="iconShiftedShiftKey" latin:keyActionFlags="noKeyPreview" latin:backgroundType="sticky" /> <key-style @@ -59,6 +58,7 @@ latin:styleName="shortcutKeyStyle" latin:code="@integer/key_shortcut" latin:keyIcon="iconShortcutKey" + latin:keyIconDisabled="iconDisabledShortcutKey" latin:keyActionFlags="noKeyPreview|altCodeWhileTyping" latin:altCode="@integer/key_dummy" latin:backgroundType="functional" /> diff --git a/java/res/xml-sw768dp/kbd_rows_russian.xml b/java/res/xml-sw768dp/kbd_rows_slavic.xml index eb0baf95d..4c9128d86 100644 --- a/java/res/xml-sw768dp/kbd_rows_russian.xml +++ b/java/res/xml-sw768dp/kbd_rows_slavic.xml @@ -34,20 +34,22 @@ <Key latin:keyLabel="ц" /> <Key - latin:keyLabel="у" /> + latin:keyLabel="у" + latin:moreKeys="@string/more_keys_for_slavic_u" /> <Key latin:keyLabel="к" /> <Key latin:keyLabel="е" - latin:moreKeys="@string/more_keys_for_cyrillic_e" /> + latin:moreKeys="@string/more_keys_for_slavic_ye" /> <Key - latin:keyLabel="н" /> + latin:keyLabel="н" + latin:moreKeys="@string/more_keys_for_slavic_en" /> <Key latin:keyLabel="г" /> <Key latin:keyLabel="ш" /> <Key - latin:keyLabel="щ" /> + latin:keyLabel="@string/keylabel_for_slavic_shcha" /> <Key latin:keyLabel="з" /> <Key @@ -68,7 +70,8 @@ <Key latin:keyLabel="ф" /> <Key - latin:keyLabel="ы" /> + latin:keyLabel="@string/keylabel_for_slavic_yery" + latin:moreKeys="@string/more_keys_for_slavic_yery" /> <Key latin:keyLabel="в" /> <Key @@ -78,7 +81,8 @@ <Key latin:keyLabel="р" /> <Key - latin:keyLabel="о" /> + latin:keyLabel="о" + latin:moreKeys="@string/more_keys_for_slavic_o" /> <Key latin:keyLabel="л" /> <Key @@ -107,7 +111,7 @@ <Key latin:keyLabel="м" /> <Key - latin:keyLabel="и" /> + latin:keyLabel="@string/keylabel_for_slavic_i" /> <Key latin:keyLabel="т" /> <Key diff --git a/java/res/xml-uk/keyboard_set.xml b/java/res/xml-uk/keyboard_set.xml new file mode 100644 index 000000000..e5ba43b23 --- /dev/null +++ b/java/res/xml-uk/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="uk"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_slavic" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-vi/keyboard_set.xml b/java/res/xml-vi/keyboard_set.xml new file mode 100644 index 000000000..7f4b25d9d --- /dev/null +++ b/java/res/xml-vi/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="vi"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_qwerty" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml index 5714e09c5..7869d21d0 100644 --- a/java/res/xml/kbd_key_styles.xml +++ b/java/res/xml/kbd_key_styles.xml @@ -66,7 +66,6 @@ latin:styleName="shiftKeyStyle" latin:code="@integer/key_shift" latin:keyIcon="iconShiftKey" - latin:keyIconShifted="iconShiftedShiftKey" latin:keyActionFlags="noKeyPreview" latin:backgroundType="sticky" /> <key-style @@ -163,6 +162,7 @@ latin:styleName="shortcutKeyStyle" latin:code="@integer/key_shortcut" latin:keyIcon="iconShortcutKey" + latin:keyIconDisabled="iconDisabledShortcutKey" latin:keyActionFlags="noKeyPreview|altCodeWhileTyping" latin:altCode="@integer/key_space" latin:parentStyle="f1PopupStyle" /> diff --git a/java/res/xml/kbd_numkey_styles.xml b/java/res/xml/kbd_numkey_styles.xml index c2ff4d50f..003165da8 100644 --- a/java/res/xml/kbd_numkey_styles.xml +++ b/java/res/xml/kbd_numkey_styles.xml @@ -124,6 +124,6 @@ <key-style latin:styleName="numSpaceKeyStyle" latin:code="@integer/key_space" - latin:keyIcon="iconSpaceKey" + latin:keyIcon="iconSpaceKeyForNumberLayout" latin:parentStyle="numKeyBaseStyle" /> </merge> diff --git a/java/res/xml/kbd_rows_russian.xml b/java/res/xml/kbd_rows_slavic.xml index f1794e750..6536eaeb3 100644 --- a/java/res/xml/kbd_rows_russian.xml +++ b/java/res/xml/kbd_rows_slavic.xml @@ -37,7 +37,7 @@ <Key latin:keyLabel="у" latin:keyHintLabel="3" - latin:moreKeys="3" /> + latin:moreKeys="@string/more_keys_for_slavic_u" /> <Key latin:keyLabel="к" latin:keyHintLabel="4" @@ -45,11 +45,11 @@ <Key latin:keyLabel="е" latin:keyHintLabel="5" - latin:moreKeys="@string/more_keys_for_cyrillic_e" /> + latin:moreKeys="@string/more_keys_for_slavic_ye" /> <Key latin:keyLabel="н" latin:keyHintLabel="6" - latin:moreKeys="6" /> + latin:moreKeys="@string/more_keys_for_slavic_en" /> <Key latin:keyLabel="г" latin:keyHintLabel="7" @@ -59,7 +59,7 @@ latin:keyHintLabel="8" latin:moreKeys="8" /> <Key - latin:keyLabel="щ" + latin:keyLabel="@string/keylabel_for_slavic_shcha" latin:keyHintLabel="9" latin:moreKeys="9" /> <Key @@ -68,7 +68,7 @@ latin:moreKeys="0" /> <Key latin:keyLabel="х" - latin:moreKeys="@string/more_keys_for_cyrillic_ha" + latin:moreKeys="@string/more_keys_for_slavic_ha" latin:keyWidth="fillRight" /> </Row> <Row @@ -78,7 +78,8 @@ latin:keyLabel="ф" latin:keyWidth="8.75%p" /> <Key - latin:keyLabel="ы" /> + latin:keyLabel="@string/keylabel_for_slavic_yery" + latin:moreKeys="@string/more_keys_for_slavic_yery" /> <Key latin:keyLabel="в" /> <Key @@ -88,7 +89,8 @@ <Key latin:keyLabel="р" /> <Key - latin:keyLabel="о" /> + latin:keyLabel="о" + latin:moreKeys="@string/more_keys_for_slavic_o" /> <Key latin:keyLabel="л" /> <Key @@ -114,12 +116,12 @@ <Key latin:keyLabel="м" /> <Key - latin:keyLabel="и" /> + latin:keyLabel="@string/keylabel_for_slavic_i" /> <Key latin:keyLabel="т" /> <Key latin:keyLabel="ь" - latin:moreKeys="@string/more_keys_for_cyrillic_soft_sign" /> + latin:moreKeys="@string/more_keys_for_slavic_soft_sign" /> <Key latin:keyLabel="б" /> <Key diff --git a/java/res/xml/kbd_russian.xml b/java/res/xml/kbd_slavic.xml index 071bfd588..6207d2939 100644 --- a/java/res/xml/kbd_russian.xml +++ b/java/res/xml/kbd_slavic.xml @@ -22,5 +22,5 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <include - latin:keyboardLayout="@xml/kbd_rows_russian" /> + latin:keyboardLayout="@xml/kbd_rows_slavic" /> </Keyboard> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 2b2c00c17..f58ad76f7 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -20,7 +20,8 @@ <!-- The attributes in this XML file provide configuration information --> <!-- for the Input Method Manager. --> -<!-- Keyboard: en_US, en_GB, ar, cs, da, de, de(QWERTY), es, es_US, et, fi, fr, fr_CA, fr_CH, hr, hu, it, iw, lt, lv, nb, nl, pl, pt, ru, sr, sv, tr --> +<!-- Keyboard: en_US, en_GB, ar, be, cs, da, de, de(QWERTY), es, es_US, et, fi, fr, fr_CA, fr_CH, + hr, hu, it, iw, ky, lt, lv, nb, nl, pl, pt, ro, ru, sk, sl, sr, sv, tr, uk, vi --> <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. --> <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default subtype.--> @@ -47,6 +48,12 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="be" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -132,6 +139,12 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="ky" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="lt" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" @@ -168,12 +181,30 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="ro" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="ru" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="sk" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:imeSubtypeLocale="sl" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection" @@ -190,4 +221,16 @@ android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:imeSubtypeLocale="uk" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" + android:imeSubtypeLocale="vi" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + /> </input-method> diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index 1836f27b3..9caed00c9 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.os.SystemClock; @@ -55,15 +54,15 @@ public class AccessibilityUtils { */ private static final boolean ENABLE_ACCESSIBILITY = true; - public static void init(InputMethodService inputMethod, SharedPreferences prefs) { + public static void init(InputMethodService inputMethod) { if (!ENABLE_ACCESSIBILITY) return; // These only need to be initialized if the kill switch is off. - sInstance.initInternal(inputMethod, prefs); - KeyCodeDescriptionMapper.init(inputMethod, prefs); - AccessibleInputMethodServiceProxy.init(inputMethod, prefs); - AccessibleKeyboardViewProxy.init(inputMethod, prefs); + sInstance.initInternal(inputMethod); + KeyCodeDescriptionMapper.init(); + AccessibleInputMethodServiceProxy.init(inputMethod); + AccessibleKeyboardViewProxy.init(inputMethod); } public static AccessibilityUtils getInstance() { @@ -74,7 +73,7 @@ public class AccessibilityUtils { // This class is not publicly instantiable. } - private void initInternal(Context context, SharedPreferences prefs) { + private void initInternal(Context context) { mContext = context; mAccessibilityManager = (AccessibilityManager) context .getSystemService(Context.ACCESSIBILITY_SERVICE); diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java index 4ab9cb898..d834dd10b 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.os.Looper; @@ -82,8 +81,8 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi } } - public static void init(InputMethodService inputMethod, SharedPreferences prefs) { - sInstance.initInternal(inputMethod, prefs); + public static void init(InputMethodService inputMethod) { + sInstance.initInternal(inputMethod); } public static AccessibleInputMethodServiceProxy getInstance() { @@ -94,7 +93,7 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi // Not publicly instantiable. } - private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) { + private void initInternal(InputMethodService inputMethod) { mInputMethod = inputMethod; mVibrator = (Vibrator) inputMethod.getSystemService(Context.VIBRATOR_SERVICE); mAudioManager = (AudioManager) inputMethod.getSystemService(Context.AUDIO_SERVICE); @@ -125,8 +124,6 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi */ @Override public void onFlickGesture(int direction) { - final int keyEventCode; - switch (direction) { case FlickGestureDetector.FLICK_LEFT: sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT); diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 4cb2f20b9..9141daaee 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Paint; import android.inputmethodservice.InputMethodService; @@ -43,8 +42,8 @@ public class AccessibleKeyboardViewProxy { private Key mLastHoverKey = null; - public static void init(InputMethodService inputMethod, SharedPreferences prefs) { - sInstance.initInternal(inputMethod, prefs); + public static void init(InputMethodService inputMethod) { + sInstance.initInternal(inputMethod); sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance(); } @@ -60,7 +59,7 @@ public class AccessibleKeyboardViewProxy { // Not publicly instantiable. } - private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) { + private void initInternal(InputMethodService inputMethod) { final Paint paint = new Paint(); paint.setTextAlign(Paint.Align.LEFT); paint.setTextSize(14.0f); @@ -71,8 +70,7 @@ public class AccessibleKeyboardViewProxy { mGestureDetector = new KeyboardFlickGestureDetector(inputMethod); } - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, - PointerTracker tracker) { + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { if (mView == null) { Log.e(TAG, "No keyboard view set!"); return false; @@ -132,9 +130,9 @@ public class AccessibleKeyboardViewProxy { final Key key = tracker.getKeyOn(x, y); if (key != mLastHoverKey) { - fireKeyHoverEvent(tracker, mLastHoverKey, false); + fireKeyHoverEvent(mLastHoverKey, false); mLastHoverKey = key; - fireKeyHoverEvent(tracker, mLastHoverKey, true); + fireKeyHoverEvent(mLastHoverKey, true); } return true; @@ -143,7 +141,7 @@ public class AccessibleKeyboardViewProxy { return false; } - private void fireKeyHoverEvent(PointerTracker tracker, Key key, boolean entering) { + private void fireKeyHoverEvent(Key key, boolean entering) { if (mListener == null) { Log.e(TAG, "No accessible keyboard action listener set!"); return; diff --git a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java index 9d99e3131..db12f76ad 100644 --- a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java +++ b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java @@ -126,7 +126,6 @@ public abstract class FlickGestureDetector { } final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event); - final long timeout = event.getEventTime() - mCachedHoverEnter.getEventTime(); switch (event.getAction()) { case MotionEventCompatUtils.ACTION_HOVER_MOVE: diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index e01262c20..3d5ab05c3 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.text.TextUtils; import com.android.inputmethod.keyboard.Key; @@ -45,8 +44,8 @@ public class KeyCodeDescriptionMapper { // Map of shift-locked key codes to spoken description resource IDs private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap; - public static void init(Context context, SharedPreferences prefs) { - sInstance.initInternal(context, prefs); + public static void init() { + sInstance.initInternal(); } public static KeyCodeDescriptionMapper getInstance() { @@ -60,7 +59,7 @@ public class KeyCodeDescriptionMapper { mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>(); } - private void initInternal(Context context, SharedPreferences prefs) { + private void initInternal() { // Manual label substitutions for key labels with no string resource mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); diff --git a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java index f6afbcfe2..011473bef 100644 --- a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java @@ -16,10 +16,14 @@ package com.android.inputmethod.compat; +import android.util.Log; + import java.lang.reflect.Method; import java.util.Arrays; public class ArraysCompatUtils { + private static final String TAG = ArraysCompatUtils.class.getSimpleName(); + private static final Method METHOD_Arrays_binarySearch = CompatUtils .getMethod(Arrays.class, "binarySearch", int[].class, int.class, int.class, int.class); @@ -33,8 +37,15 @@ public class ArraysCompatUtils { } } - /* package */ static int compatBinarySearch(int[] array, int startIndex, int endIndex, - int value) { + // TODO: Implement fast binary search + /* package for testing */ + static int compatBinarySearch(int[] array, int startIndex, int endIndex, int value) { + // Output error log because this method has strict performance penalty. + // Note that this method has been called only from spell checker and spell checker exists + // only from IceCreamSandwich and after, so that there is no chance on pre-ICS device to + // invoke this method. + Log.e(TAG, "Invoked expensive binarySearch"); + if (startIndex > endIndex) throw new IllegalArgumentException(); if (startIndex < 0 || endIndex > array.length) throw new ArrayIndexOutOfBoundsException(); diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 4e4ccef18..e1e74fc9a 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -27,16 +27,15 @@ import android.util.Xml; import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; -import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; -import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; -import com.android.inputmethod.keyboard.internal.XmlParseUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -73,10 +72,13 @@ public class Key { private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; + // TODO: These icon references could be int (icon attribute id) /** Icon to display instead of a label. Icon takes precedence over a label */ private Drawable mIcon; + /** Icon for disabled state */ + private Drawable mDisabledIcon; /** Preview version of the icon, for the preview popup */ - private Drawable mPreviewIcon; + public final Drawable mPreviewIcon; /** Width of the key, not including the gap */ public final int mWidth; @@ -115,14 +117,14 @@ public class Key { private static final int ACTION_FLAGS_NO_KEY_PREVIEW = 0x02; private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04; + private final int mHashCode; + /** The current pressed state of this key */ private boolean mPressed; /** If this is a sticky key, is its highlight on? */ private boolean mHighlightOn; /** Key is enabled and responds on press */ private boolean mEnabled = true; - /** Whether this key needs to show the "..." popup hint for special purposes */ - private boolean mNeedsSpecialPopupHint; // RTL parenthesis character swapping map. private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>(); @@ -158,19 +160,19 @@ public class Key { } } - private static int getCode(Resources res, KeyboardParams params, String moreKeySpec) { + private static int getCode(Resources res, Keyboard.Params params, String moreKeySpec) { return getRtlParenthesisCode( MoreKeySpecParser.getCode(res, moreKeySpec), params.mIsRtlKeyboard); } - private static Drawable getIcon(KeyboardParams params, String moreKeySpec) { - return params.mIconsSet.getIcon(MoreKeySpecParser.getIconId(moreKeySpec)); + private static Drawable getIcon(Keyboard.Params params, String moreKeySpec) { + return params.mIconsSet.getIconByIconId(MoreKeySpecParser.getIconId(moreKeySpec)); } /** * This constructor is being used only for key in more keys keyboard. */ - public Key(Resources res, KeyboardParams params, String moreKeySpec, + public Key(Resources res, Keyboard.Params params, String moreKeySpec, int x, int y, int width, int height) { this(params, MoreKeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec), getCode(res, params, moreKeySpec), MoreKeySpecParser.getOutputText(moreKeySpec), @@ -180,7 +182,7 @@ public class Key { /** * This constructor is being used only for key in popup suggestions pane. */ - public Key(KeyboardParams params, CharSequence label, CharSequence hintLabel, Drawable icon, + public Key(Keyboard.Params params, CharSequence label, CharSequence hintLabel, Drawable icon, int code, CharSequence outputText, int x, int y, int width, int height) { mHeight = height - params.mVerticalGap; mHorizontalGap = params.mHorizontalGap; @@ -198,10 +200,14 @@ public class Key { mCode = code; mAltCode = Keyboard.CODE_DUMMY; mIcon = icon; + mDisabledIcon = null; + mPreviewIcon = null; // Horizontal gap is divided equally to both sides of the key. mX = x + mHorizontalGap / 2; mY = y; mHitBox.set(x, y, x + width + 1, y + height); + + mHashCode = hashCode(this); } /** @@ -215,7 +221,7 @@ public class Key { * @param keyStyles active key styles set * @throws XmlPullParserException */ - public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row, + public Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException { final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap; final int keyHeight = row.mRowHeight; @@ -267,20 +273,16 @@ public class Key { mActionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0); final KeyboardIconsSet iconsSet = params.mIconsSet; - mVisualInsetsLeft = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr, + mVisualInsetsLeft = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0); - mVisualInsetsRight = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr, + mVisualInsetsRight = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0); - mPreviewIcon = iconsSet.getIcon(style.getInt(keyAttr, + mPreviewIcon = iconsSet.getIconByIconId(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED)); - mIcon = iconsSet.getIcon(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIcon, - KeyboardIconsSet.ICON_UNDEFINED)); - final int shiftedIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted, - KeyboardIconsSet.ICON_UNDEFINED); - if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) { - final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId); - params.addShiftedIcon(this, shiftedIcon); - } + mIcon = iconsSet.getIconByIconId(style.getInt(keyAttr, + R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED)); + mDisabledIcon = iconsSet.getIconByIconId(style.getInt(keyAttr, + R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED)); mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); @@ -304,23 +306,71 @@ public class Key { } mAltCode = style.getInt(keyAttr, R.styleable.Keyboard_Key_altCode, Keyboard.CODE_DUMMY); + mHashCode = hashCode(this); keyAttr.recycle(); } - public void markAsLeftEdge(KeyboardParams params) { + private static int hashCode(Key key) { + return Arrays.hashCode(new Object[] { + key.mX, + key.mY, + key.mWidth, + key.mHeight, + key.mCode, + key.mLabel, + key.mHintLabel, + // Key can be distinguishable without the following members. + // key.mAltCode, + // key.mOutputText, + // key.mActionFlags, + // key.mLabelFlags, + // key.mIcon, + // key.mPreviewIcon, + // key.mBackgroundType, + // key.mHorizontalGap, + // key.mVerticalGap, + // key.mVisualInsetLeft, + // key.mVisualInsetRight, + // Arrays.hashCode(key.mMoreKeys), + // key.mMaxMoreKeysColumn, + }); + } + + private boolean equals(Key o) { + if (this == o) return true; + return o.mX == mX + && o.mY == mY + && o.mWidth == mWidth + && o.mHeight == mHeight + && o.mCode == mCode + && TextUtils.equals(o.mLabel, mLabel) + && TextUtils.equals(o.mHintLabel, mHintLabel); + } + + @Override + public int hashCode() { + return mHashCode; + } + + @Override + public boolean equals(Object o) { + return o instanceof Key && equals((Key)o); + } + + public void markAsLeftEdge(Keyboard.Params params) { mHitBox.left = params.mHorizontalEdgesPadding; } - public void markAsRightEdge(KeyboardParams params) { + public void markAsRightEdge(Keyboard.Params params) { mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding; } - public void markAsTopEdge(KeyboardParams params) { + public void markAsTopEdge(Keyboard.Params params) { mHitBox.top = params.mTopPadding; } - public void markAsBottomEdge(KeyboardParams params) { + public void markAsBottomEdge(Keyboard.Params params) { mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; } @@ -393,14 +443,6 @@ public class Key { return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; } - public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) { - mNeedsSpecialPopupHint = needsSpecialPopupHint; - } - - public boolean needsSpecialPopupHint() { - return mNeedsSpecialPopupHint; - } - public boolean hasUppercaseLetter() { return (mLabelFlags & LABEL_FLAGS_HAS_UPPERCASE_LETTER) != 0; } @@ -422,21 +464,14 @@ public class Key { } public Drawable getIcon() { - return mIcon; - } - - public Drawable getPreviewIcon() { - return mPreviewIcon; + return mEnabled ? mIcon : mDisabledIcon; } + // TODO: Get rid of this method. public void setIcon(Drawable icon) { mIcon = icon; } - public void setPreviewIcon(Drawable icon) { - mPreviewIcon = icon; - } - /** * Informs the key that it has been pressed, in case it needs to change its appearance or * state. @@ -571,7 +606,7 @@ public class Key { } public static class Spacer extends Key { - public Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row, + public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException { super(res, params, row, parser, keyStyles); } @@ -579,7 +614,7 @@ public class Key { /** * This constructor is being used only for divider in more keys keyboard. */ - public Spacer(KeyboardParams params, Drawable icon, int x, int y, int width, int height) { + public Spacer(Keyboard.Params params, Drawable icon, int x, int y, int width, int height) { super(params, null, null, icon, Keyboard.CODE_DUMMY, null, x, y, width, height); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 8e325b65c..0d271625b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -19,14 +19,12 @@ package com.android.inputmethod.keyboard; import android.util.Log; import java.util.Arrays; -import java.util.List; public class KeyDetector { private static final String TAG = KeyDetector.class.getSimpleName(); private static final boolean DEBUG = false; public static final int NOT_A_CODE = -1; - private static final int NOT_A_KEY = -1; private final int mKeyHysteresisDistanceSquared; @@ -39,7 +37,7 @@ public class KeyDetector { // working area private static final int MAX_NEARBY_KEYS = 12; private final int[] mDistances = new int[MAX_NEARBY_KEYS]; - private final int[] mIndices = new int[MAX_NEARBY_KEYS]; + private final Key[] mNeighborKeys = new Key[MAX_NEARBY_KEYS]; /** * This class handles key detection. @@ -122,7 +120,7 @@ public class KeyDetector { private void initializeNearbyKeys() { Arrays.fill(mDistances, Integer.MAX_VALUE); - Arrays.fill(mIndices, NOT_A_KEY); + Arrays.fill(mNeighborKeys, null); } /** @@ -130,14 +128,14 @@ public class KeyDetector { * If the distance of two keys are the same, the key which the point is on should be considered * as a closer one. * - * @param keyIndex index of the key. + * @param key the key to be inserted into the nearby keys buffer. * @param distance distance between the key's edge and user touched point. * @param isOnKey true if the point is on the key. * @return order of the key in the nearby buffer, 0 if it is the nearest key. */ - private int sortNearbyKeys(int keyIndex, int distance, boolean isOnKey) { + private int sortNearbyKeys(Key key, int distance, boolean isOnKey) { final int[] distances = mDistances; - final int[] indices = mIndices; + final Key[] neighborKeys = mNeighborKeys; for (int insertPos = 0; insertPos < distances.length; insertPos++) { final int comparingDistance = distances[insertPos]; if (distance < comparingDistance || (distance == comparingDistance && isOnKey)) { @@ -145,11 +143,11 @@ public class KeyDetector { if (nextPos < distances.length) { System.arraycopy(distances, insertPos, distances, nextPos, distances.length - nextPos); - System.arraycopy(indices, insertPos, indices, nextPos, - indices.length - nextPos); + System.arraycopy(neighborKeys, insertPos, neighborKeys, nextPos, + neighborKeys.length - nextPos); } distances[insertPos] = distance; - indices[insertPos] = keyIndex; + neighborKeys[insertPos] = key; return insertPos; } } @@ -157,21 +155,20 @@ public class KeyDetector { } private void getNearbyKeyCodes(final int[] allCodes) { - final List<Key> keys = getKeyboard().mKeys; - final int[] indices = mIndices; + final Key[] neighborKeys = mNeighborKeys; // allCodes[0] should always have the key code even if it is a non-letter key. - if (indices[0] == NOT_A_KEY) { + if (neighborKeys[0] == null) { allCodes[0] = NOT_A_CODE; return; } int numCodes = 0; - for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) { - final int index = indices[j]; - if (index == NOT_A_KEY) + for (int j = 0; j < neighborKeys.length && numCodes < allCodes.length; j++) { + final Key key = neighborKeys[j]; + if (key == null) break; - final int code = keys.get(index).mCode; + final int code = key.mCode; // filter out a non-letter key from nearby keys if (code < Keyboard.CODE_SPACE) continue; @@ -191,18 +188,16 @@ public class KeyDetector { * @return The nearest key */ public Key getKeyAndNearbyCodes(int x, int y, final int[] allCodes) { - final List<Key> keys = getKeyboard().mKeys; final int touchX = getTouchX(x); final int touchY = getTouchY(y); initializeNearbyKeys(); Key primaryKey = null; - for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) { - final Key key = keys.get(index); + for (final Key key: mKeyboard.getNearestKeys(touchX, touchY)) { final boolean isOnKey = key.isOnKey(touchX, touchY); final int distance = key.squaredDistanceToEdge(touchX, touchY); if (isOnKey || (mProximityCorrectOn && distance < mProximityThresholdSquare)) { - final int insertedPosition = sortNearbyKeys(index, distance, isOnKey); + final int insertedPosition = sortNearbyKeys(key, distance, isOnKey); if (insertedPosition == 0 && isOnKey) { primaryKey = key; } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 814b52398..d9d28f186 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -16,17 +16,33 @@ package com.android.inputmethod.keyboard; -import android.graphics.drawable.Drawable; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; +import android.view.InflateException; +import com.android.inputmethod.compat.EditorInfoCompatUtils; +import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; -import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.KeyboardShiftState; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.XmlParseUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -106,11 +122,9 @@ public class Keyboard { public final boolean mIsRtlKeyboard; /** List of keys and icons in this keyboard */ - public final List<Key> mKeys; - public final List<Key> mShiftKeys; + public final Set<Key> mKeys; + public final Set<Key> mShiftKeys; public final Set<Key> mShiftLockKeys; - public final Map<Key, Drawable> mShiftedIcons; - public final Map<Key, Drawable> mUnshiftedIcons; public final KeyboardIconsSet mIconsSet; private final Map<Integer, Key> mKeyCache = new HashMap<Integer, Key>(); @@ -120,7 +134,7 @@ public class Keyboard { // TODO: Remove this variable. private final KeyboardShiftState mShiftState = new KeyboardShiftState(); - public Keyboard(KeyboardParams params) { + public Keyboard(Params params) { mId = params.mId; mThemeId = params.mThemeId; mOccupiedHeight = params.mOccupiedHeight; @@ -134,11 +148,9 @@ public class Keyboard { mTopPadding = params.mTopPadding; mVerticalGap = params.mVerticalGap; - mKeys = Collections.unmodifiableList(params.mKeys); - mShiftKeys = Collections.unmodifiableList(params.mShiftKeys); + mKeys = Collections.unmodifiableSet(params.mKeys); + mShiftKeys = Collections.unmodifiableSet(params.mShiftKeys); mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys); - mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons); - mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons); mIconsSet = params.mIconsSet; mProximityInfo = new ProximityInfo( @@ -170,17 +182,20 @@ public class Keyboard { } // TODO: Remove this method. - public boolean hasShiftLockKey() { + boolean hasShiftLockKey() { return !mShiftLockKeys.isEmpty(); } // TODO: Remove this method. - public void setShiftLocked(boolean newShiftLockState) { + void setShiftLocked(boolean newShiftLockState) { for (final Key key : mShiftLockKeys) { // To represent "shift locked" state. The highlight is handled by background image that // might be a StateListDrawable. key.setHighlightOn(newShiftLockState); - key.setIcon(newShiftLockState ? mShiftedIcons.get(key) : mUnshiftedIcons.get(key)); + final int attrId = newShiftLockState + ? R.styleable.Keyboard_iconShiftKeyShifted + : R.styleable.Keyboard_iconShiftKey; + key.setIcon(mIconsSet.getIconByAttrId(attrId)); } mShiftState.setShiftLocked(newShiftLockState); } @@ -191,10 +206,13 @@ public class Keyboard { } // TODO: Remove this method. - public void setShifted(boolean newShiftState) { + void setShifted(boolean newShiftState) { if (!mShiftState.isShiftLocked()) { for (final Key key : mShiftKeys) { - key.setIcon(newShiftState ? mShiftedIcons.get(key) : mUnshiftedIcons.get(key)); + final int attrId = newShiftState + ? R.styleable.Keyboard_iconShiftKeyShifted + : R.styleable.Keyboard_iconShiftKey; + key.setIcon(mIconsSet.getIconByAttrId(attrId)); } } mShiftState.setShifted(newShiftState); @@ -206,7 +224,7 @@ public class Keyboard { } // TODO: Remove this method - public void setAutomaticTemporaryUpperCase() { + void setAutomaticTemporaryUpperCase() { mShiftState.setAutomaticTemporaryUpperCase(); } @@ -217,22 +235,178 @@ public class Keyboard { // TODO: Remove this method. public CharSequence adjustLabelCase(CharSequence label) { - if (isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) && label.length() < 3 - && Character.isLowerCase(label.charAt(0))) { + if (mId.isAlphabetKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) + && label.length() < 3 && Character.isLowerCase(label.charAt(0))) { return label.toString().toUpperCase(mId.mLocale); } return label; } + public static class Params { + public KeyboardId mId; + public int mThemeId; + + /** Total height and width of the keyboard, including the paddings and keys */ + public int mOccupiedHeight; + public int mOccupiedWidth; + + /** Base height and width of the keyboard used to calculate rows' or keys' heights and + * widths + */ + public int mBaseHeight; + public int mBaseWidth; + + public int mTopPadding; + public int mBottomPadding; + public int mHorizontalEdgesPadding; + public int mHorizontalCenterPadding; + + public int mDefaultRowHeight; + public int mDefaultKeyWidth; + public int mHorizontalGap; + public int mVerticalGap; + + public boolean mIsRtlKeyboard; + public int mMoreKeysTemplate; + public int mMaxMiniKeyboardColumn; + + public int GRID_WIDTH; + public int GRID_HEIGHT; + + public final Set<Key> mKeys = new HashSet<Key>(); + public final Set<Key> mShiftKeys = new HashSet<Key>(); + public final Set<Key> mShiftLockKeys = new HashSet<Key>(); + public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); + + public int mMostCommonKeyHeight = 0; + public int mMostCommonKeyWidth = 0; + + public final TouchPositionCorrection mTouchPositionCorrection = + new TouchPositionCorrection(); + + public static class TouchPositionCorrection { + private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3; + + public boolean mEnabled; + public float[] mXs; + public float[] mYs; + public float[] mRadii; + + public void load(String[] data) { + final int dataLength = data.length; + if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) { + if (LatinImeLogger.sDBG) + throw new RuntimeException( + "the size of touch position correction data is invalid"); + return; + } + + final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE; + mXs = new float[length]; + mYs = new float[length]; + mRadii = new float[length]; + try { + for (int i = 0; i < dataLength; ++i) { + final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE; + final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE; + final float value = Float.parseFloat(data[i]); + if (type == 0) { + mXs[index] = value; + } else if (type == 1) { + mYs[index] = value; + } else { + mRadii[index] = value; + } + } + } catch (NumberFormatException e) { + if (LatinImeLogger.sDBG) { + throw new RuntimeException( + "the number format for touch position correction data is invalid"); + } + mXs = null; + mYs = null; + mRadii = null; + } + } + + public void setEnabled(boolean enabled) { + mEnabled = enabled; + } + + public boolean isValid() { + return mEnabled && mXs != null && mYs != null && mRadii != null + && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0; + } + } + + protected void clearKeys() { + mKeys.clear(); + mShiftKeys.clear(); + mShiftLockKeys.clear(); + clearHistogram(); + } + + public void onAddKey(Key key) { + mKeys.add(key); + updateHistogram(key); + if (key.mCode == Keyboard.CODE_SHIFT) { + mShiftKeys.add(key); + if (key.isSticky()) { + mShiftLockKeys.add(key); + } + } + } + + private int mMaxHeightCount = 0; + private int mMaxWidthCount = 0; + private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>(); + private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>(); + + private void clearHistogram() { + mMostCommonKeyHeight = 0; + mMaxHeightCount = 0; + mHeightHistogram.clear(); + + mMaxWidthCount = 0; + mMostCommonKeyWidth = 0; + mWidthHistogram.clear(); + } + + private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) { + final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1; + histogram.put(key, count); + return count; + } + + private void updateHistogram(Key key) { + final Integer height = key.mHeight + key.mVerticalGap; + final int heightCount = updateHistogramCounter(mHeightHistogram, height); + if (heightCount > mMaxHeightCount) { + mMaxHeightCount = heightCount; + mMostCommonKeyHeight = height; + } + + final Integer width = key.mWidth + key.mHorizontalGap; + final int widthCount = updateHistogramCounter(mWidthHistogram, width); + if (widthCount > mMaxWidthCount) { + mMaxWidthCount = widthCount; + mMostCommonKeyWidth = width; + } + } + } + /** - * Returns the indices of the keys that are closest to the given point. + * Returns the array of the keys that are closest to the given point. * @param x the x-coordinate of the point * @param y the y-coordinate of the point - * @return the array of integer indices for the nearest keys to the given point. If the given + * @return the array of the nearest keys to the given point. If the given * point is out of range, then an array of size zero is returned. */ - public int[] getNearestKeys(int x, int y) { - return mProximityInfo.getNearestKeys(x, y); + public Key[] getNearestKeys(int x, int y) { + // Avoid dead pixels at edges of the keyboard + final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1)); + final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1)); + return mProximityInfo.getNearestKeys(adjustedX, adjustedY); } public static String printableCode(int code) { @@ -250,4 +424,812 @@ public class Keyboard { return String.format("\\u04x", code); } } + + /** + * Keyboard Building helper. + * + * This class parses Keyboard XML file and eventually build a Keyboard. + * The Keyboard XML file looks like: + * <pre> + * >!-- xml/keyboard.xml --< + * >Keyboard keyboard_attributes*< + * >!-- Keyboard Content --< + * >Row row_attributes*< + * >!-- Row Content --< + * >Key key_attributes* /< + * >Spacer horizontalGap="0.2in" /< + * >include keyboardLayout="@xml/other_keys"< + * ... + * >/Row< + * >include keyboardLayout="@xml/other_rows"< + * ... + * >/Keyboard< + * </pre> + * The XML file which is included in other file must have >merge< as root element, + * such as: + * <pre> + * >!-- xml/other_keys.xml --< + * >merge< + * >Key key_attributes* /< + * ... + * >/merge< + * </pre> + * and + * <pre> + * >!-- xml/other_rows.xml --< + * >merge< + * >Row row_attributes*< + * >Key key_attributes* /< + * >/Row< + * ... + * >/merge< + * </pre> + * You can also use switch-case-default tags to select Rows and Keys. + * <pre> + * >switch< + * >case case_attribute*< + * >!-- Any valid tags at switch position --< + * >/case< + * ... + * >default< + * >!-- Any valid tags at switch position --< + * >/default< + * >/switch< + * </pre> + * You can declare Key style and specify styles within Key tags. + * <pre> + * >switch< + * >case mode="email"< + * >key-style styleName="f1-key" parentStyle="modifier-key" + * keyLabel=".com" + * /< + * >/case< + * >case mode="url"< + * >key-style styleName="f1-key" parentStyle="modifier-key" + * keyLabel="http://" + * /< + * >/case< + * >/switch< + * ... + * >Key keyStyle="shift-key" ... /< + * </pre> + */ + + public static class Builder<KP extends Params> { + private static final String TAG = Builder.class.getSimpleName(); + private static final boolean DEBUG = false; + + // Keyboard XML Tags + private static final String TAG_KEYBOARD = "Keyboard"; + private static final String TAG_ROW = "Row"; + private static final String TAG_KEY = "Key"; + private static final String TAG_SPACER = "Spacer"; + private static final String TAG_INCLUDE = "include"; + private static final String TAG_MERGE = "merge"; + private static final String TAG_SWITCH = "switch"; + private static final String TAG_CASE = "case"; + private static final String TAG_DEFAULT = "default"; + public static final String TAG_KEY_STYLE = "key-style"; + + private static final int DEFAULT_KEYBOARD_COLUMNS = 10; + private static final int DEFAULT_KEYBOARD_ROWS = 4; + + protected final KP mParams; + protected final Context mContext; + protected final Resources mResources; + private final DisplayMetrics mDisplayMetrics; + + private int mCurrentY = 0; + private Row mCurrentRow = null; + private boolean mLeftEdge; + private boolean mTopEdge; + private Key mRightEdgeKey = null; + private final KeyStyles mKeyStyles = new KeyStyles(); + + /** + * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate. + * Some of the key size defaults can be overridden per row from what the {@link Keyboard} + * defines. + */ + public static class Row { + // keyWidth enum constants + private static final int KEYWIDTH_NOT_ENUM = 0; + private static final int KEYWIDTH_FILL_RIGHT = -1; + private static final int KEYWIDTH_FILL_BOTH = -2; + + private final Params mParams; + /** Default width of a key in this row. */ + public final float mDefaultKeyWidth; + /** Default height of a key in this row. */ + public final int mRowHeight; + + private final int mCurrentY; + // Will be updated by {@link Key}'s constructor. + private float mCurrentX; + + public Row(Resources res, Params params, XmlPullParser parser, int y) { + mParams = params; + TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard); + mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_rowHeight, + params.mBaseHeight, params.mDefaultRowHeight); + keyboardAttr.recycle(); + TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Key); + mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyWidth, + params.mBaseWidth, params.mDefaultKeyWidth); + keyAttr.recycle(); + + mCurrentY = y; + mCurrentX = 0.0f; + } + + public void setXPos(float keyXPos) { + mCurrentX = keyXPos; + } + + public void advanceXPos(float width) { + mCurrentX += width; + } + + public int getKeyY() { + return mCurrentY; + } + + public float getKeyX(TypedArray keyAttr) { + final int widthType = Builder.getEnumValue(keyAttr, + R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM); + if (widthType == KEYWIDTH_FILL_BOTH) { + // If keyWidth is fillBoth, the key width should start right after the nearest + // key on the left hand side. + return mCurrentX; + } + + final int keyboardRightEdge = mParams.mOccupiedWidth + - mParams.mHorizontalEdgesPadding; + if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) { + final float keyXPos = Builder.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0); + if (keyXPos < 0) { + // If keyXPos is negative, the actual x-coordinate will be + // keyboardWidth + keyXPos. + // keyXPos shouldn't be less than mCurrentX because drawable area for this + // key starts at mCurrentX. Or, this key will overlaps the adjacent key on + // its left hand side. + return Math.max(keyXPos + keyboardRightEdge, mCurrentX); + } else { + return keyXPos + mParams.mHorizontalEdgesPadding; + } + } + return mCurrentX; + } + + public float getKeyWidth(TypedArray keyAttr, float keyXPos) { + final int widthType = Builder.getEnumValue(keyAttr, + R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM); + switch (widthType) { + case KEYWIDTH_FILL_RIGHT: + case KEYWIDTH_FILL_BOTH: + final int keyboardRightEdge = + mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding; + // If keyWidth is fillRight, the actual key width will be determined to fill + // out the area up to the right edge of the keyboard. + // If keyWidth is fillBoth, the actual key width will be determined to fill out + // the area between the nearest key on the left hand side and the right edge of + // the keyboard. + return keyboardRightEdge - keyXPos; + default: // KEYWIDTH_NOT_ENUM + return Builder.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyWidth, + mParams.mBaseWidth, mDefaultKeyWidth); + } + } + } + + public Builder(Context context, KP params) { + mContext = context; + final Resources res = context.getResources(); + mResources = res; + mDisplayMetrics = res.getDisplayMetrics(); + + mParams = params; + + setTouchPositionCorrectionData(context, params); + + params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); + params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); + } + + private static void setTouchPositionCorrectionData(Context context, Params params) { + final TypedArray a = context.obtainStyledAttributes( + null, R.styleable.Keyboard, R.attr.keyboardStyle, 0); + params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0); + final int resourceId = a.getResourceId( + R.styleable.Keyboard_touchPositionCorrectionData, 0); + a.recycle(); + if (resourceId == 0) { + if (LatinImeLogger.sDBG) + throw new RuntimeException("touchPositionCorrectionData is not defined"); + return; + } + + final String[] data = context.getResources().getStringArray(resourceId); + params.mTouchPositionCorrection.load(data); + } + + public Builder<KP> load(int xmlId, KeyboardId id) { + mParams.mId = id; + final XmlResourceParser parser = mResources.getXml(xmlId); + try { + parseKeyboard(parser); + } catch (XmlPullParserException e) { + Log.w(TAG, "keyboard XML parse error: " + e); + throw new IllegalArgumentException(e); + } catch (IOException e) { + Log.w(TAG, "keyboard XML parse error: " + e); + throw new RuntimeException(e); + } finally { + parser.close(); + } + return this; + } + + public void setTouchPositionCorrectionEnabled(boolean enabled) { + mParams.mTouchPositionCorrection.setEnabled(enabled); + } + + public Keyboard build() { + return new Keyboard(mParams); + } + + private void parseKeyboard(XmlPullParser parser) + throws XmlPullParserException, IOException { + if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId)); + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_KEYBOARD.equals(tag)) { + parseKeyboardAttributes(parser); + startKeyboard(); + parseKeyboardContent(parser, false); + break; + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD); + } + } + } + } + + private void parseKeyboardAttributes(XmlPullParser parser) { + final int displayWidth = mDisplayMetrics.widthPixels; + final TypedArray keyboardAttr = mContext.obtainStyledAttributes( + Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle, + R.style.Keyboard); + final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Key); + try { + final int displayHeight = mDisplayMetrics.heightPixels; + final int keyboardHeight = (int)keyboardAttr.getDimension( + R.styleable.Keyboard_keyboardHeight, displayHeight / 2); + final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2); + int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2); + if (minKeyboardHeight < 0) { + // Specified fraction was negative, so it should be calculated against display + // width. + minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2); + } + final Params params = mParams; + // Keyboard height will not exceed maxKeyboardHeight and will not be less than + // minKeyboardHeight. + params.mOccupiedHeight = Math.max( + Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); + params.mOccupiedWidth = params.mId.mWidth; + params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0); + params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0); + params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyboardHorizontalEdgesPadding, + mParams.mOccupiedWidth, 0); + + params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2 + - params.mHorizontalCenterPadding; + params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth, + params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS); + params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0); + params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0); + params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding + - params.mBottomPadding + params.mVerticalGap; + params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_rowHeight, params.mBaseHeight, + params.mBaseHeight / DEFAULT_KEYBOARD_ROWS); + + params.mIsRtlKeyboard = keyboardAttr.getBoolean( + R.styleable.Keyboard_isRtlKeyboard, false); + params.mMoreKeysTemplate = keyboardAttr.getResourceId( + R.styleable.Keyboard_moreKeysTemplate, 0); + params.mMaxMiniKeyboardColumn = keyAttr.getInt( + R.styleable.Keyboard_Key_maxMoreKeysColumn, 5); + + params.mIconsSet.loadIcons(keyboardAttr); + } finally { + keyAttr.recycle(); + keyboardAttr.recycle(); + } + } + + private void parseKeyboardContent(XmlPullParser parser, boolean skip) + throws XmlPullParserException, IOException { + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_ROW.equals(tag)) { + Row row = parseRowAttributes(parser); + if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW)); + if (!skip) + startRow(row); + parseRowContent(parser, row, skip); + } else if (TAG_INCLUDE.equals(tag)) { + parseIncludeKeyboardContent(parser, skip); + } else if (TAG_SWITCH.equals(tag)) { + parseSwitchKeyboardContent(parser, skip); + } else if (TAG_KEY_STYLE.equals(tag)) { + parseKeyStyle(parser, skip); + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW); + } + } else if (event == XmlPullParser.END_TAG) { + final String tag = parser.getName(); + if (TAG_KEYBOARD.equals(tag)) { + endKeyboard(); + break; + } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) + || TAG_MERGE.equals(tag)) { + if (DEBUG) Log.d(TAG, String.format("</%s>", tag)); + break; + } else if (TAG_KEY_STYLE.equals(tag)) { + continue; + } else { + throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW); + } + } + } + } + + private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException { + final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard); + try { + if (a.hasValue(R.styleable.Keyboard_horizontalGap)) + throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap"); + if (a.hasValue(R.styleable.Keyboard_verticalGap)) + throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap"); + return new Row(mResources, mParams, parser, mCurrentY); + } finally { + a.recycle(); + } + } + + private void parseRowContent(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_KEY.equals(tag)) { + parseKey(parser, row, skip); + } else if (TAG_SPACER.equals(tag)) { + parseSpacer(parser, row, skip); + } else if (TAG_INCLUDE.equals(tag)) { + parseIncludeRowContent(parser, row, skip); + } else if (TAG_SWITCH.equals(tag)) { + parseSwitchRowContent(parser, row, skip); + } else if (TAG_KEY_STYLE.equals(tag)) { + parseKeyStyle(parser, skip); + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY); + } + } else if (event == XmlPullParser.END_TAG) { + final String tag = parser.getName(); + if (TAG_ROW.equals(tag)) { + if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW)); + if (!skip) + endRow(row); + break; + } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) + || TAG_MERGE.equals(tag)) { + if (DEBUG) Log.d(TAG, String.format("</%s>", tag)); + break; + } else if (TAG_KEY_STYLE.equals(tag)) { + continue; + } else { + throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); + } + } + } + } + + private void parseKey(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + if (skip) { + XmlParseUtils.checkEndTag(TAG_KEY, parser); + } else { + final Key key = new Key(mResources, mParams, row, parser, mKeyStyles); + if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />", + TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode, + Arrays.toString(key.mMoreKeys))); + XmlParseUtils.checkEndTag(TAG_KEY, parser); + endKey(key); + } + } + + private void parseSpacer(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + if (skip) { + XmlParseUtils.checkEndTag(TAG_SPACER, parser); + } else { + final Key.Spacer spacer = new Key.Spacer( + mResources, mParams, row, parser, mKeyStyles); + if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); + XmlParseUtils.checkEndTag(TAG_SPACER, parser); + endKey(spacer); + } + } + + private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip) + throws XmlPullParserException, IOException { + parseIncludeInternal(parser, null, skip); + } + + private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + parseIncludeInternal(parser, row, skip); + } + + private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + if (skip) { + XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); + } else { + final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Include); + int keyboardLayout = 0; + try { + XmlParseUtils.checkAttributeExists(a, + R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout", + TAG_INCLUDE, parser); + keyboardLayout = a.getResourceId( + R.styleable.Keyboard_Include_keyboardLayout, 0); + } finally { + a.recycle(); + } + + XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); + if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", + TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); + final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout); + try { + parseMerge(parserForInclude, row, skip); + } finally { + parserForInclude.close(); + } + } + } + + private void parseMerge(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_MERGE.equals(tag)) { + if (row == null) { + parseKeyboardContent(parser, skip); + } else { + parseRowContent(parser, row, skip); + } + break; + } else { + throw new XmlParseUtils.ParseException( + "Included keyboard layout must have <merge> root element", parser); + } + } + } + } + + private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip) + throws XmlPullParserException, IOException { + parseSwitchInternal(parser, null, skip); + } + + private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + parseSwitchInternal(parser, row, skip); + } + + private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId)); + boolean selected = false; + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_CASE.equals(tag)) { + selected |= parseCase(parser, row, selected ? true : skip); + } else if (TAG_DEFAULT.equals(tag)) { + selected |= parseDefault(parser, row, selected ? true : skip); + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY); + } + } else if (event == XmlPullParser.END_TAG) { + final String tag = parser.getName(); + if (TAG_SWITCH.equals(tag)) { + if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH)); + break; + } else { + throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); + } + } + } + } + + private boolean parseCase(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + final boolean selected = parseCaseCondition(parser); + if (row == null) { + // Processing Rows. + parseKeyboardContent(parser, selected ? skip : true); + } else { + // Processing Keys. + parseRowContent(parser, row, selected ? skip : true); + } + return selected; + } + + private boolean parseCaseCondition(XmlPullParser parser) { + final KeyboardId id = mParams.mId; + if (id == null) + return true; + + final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Case); + try { + final boolean modeMatched = matchTypedValue(a, + R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode)); + final boolean navigateActionMatched = matchBoolean(a, + R.styleable.Keyboard_Case_navigateAction, id.navigateAction()); + final boolean passwordInputMatched = matchBoolean(a, + R.styleable.Keyboard_Case_passwordInput, id.passwordInput()); + final boolean hasSettingsKeyMatched = matchBoolean(a, + R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey()); + final boolean f2KeyModeMatched = matchInteger(a, + R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode()); + final boolean clobberSettingsKeyMatched = matchBoolean(a, + R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); + final boolean shortcutKeyEnabledMatched = matchBoolean(a, + R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled); + final boolean hasShortcutKeyMatched = matchBoolean(a, + R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); + // As noted at {@link KeyboardId} class, we are interested only in enum value + // masked by {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and + // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching + // this attribute with id.mImeOptions as integer value is enough for our purpose. + final boolean imeActionMatched = matchInteger(a, + R.styleable.Keyboard_Case_imeAction, id.imeAction()); + final boolean localeCodeMatched = matchString(a, + R.styleable.Keyboard_Case_localeCode, id.mLocale.toString()); + final boolean languageCodeMatched = matchString(a, + R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage()); + final boolean countryCodeMatched = matchString(a, + R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); + final boolean selected = modeMatched && navigateActionMatched + && passwordInputMatched && hasSettingsKeyMatched && f2KeyModeMatched + && clobberSettingsKeyMatched && shortcutKeyEnabledMatched + && hasShortcutKeyMatched && imeActionMatched && localeCodeMatched + && languageCodeMatched && countryCodeMatched; + + if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, + textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"), + booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"), + booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), + booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"), + textAttr(KeyboardId.f2KeyModeName( + a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"), + booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey, + "clobberSettingsKey"), + booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled, + "shortcutKeyEnabled"), + booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), + textAttr(EditorInfoCompatUtils.imeOptionsName( + a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"), + textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"), + textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), + "languageCode"), + textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"), + Boolean.toString(selected))); + + return selected; + } finally { + a.recycle(); + } + } + + private static boolean matchInteger(TypedArray a, int index, int value) { + // If <case> does not have "index" attribute, that means this <case> is wild-card for + // the attribute. + return !a.hasValue(index) || a.getInt(index, 0) == value; + } + + private static boolean matchBoolean(TypedArray a, int index, boolean value) { + // If <case> does not have "index" attribute, that means this <case> is wild-card for + // the attribute. + return !a.hasValue(index) || a.getBoolean(index, false) == value; + } + + private static boolean matchString(TypedArray a, int index, String value) { + // If <case> does not have "index" attribute, that means this <case> is wild-card for + // the attribute. + return !a.hasValue(index) + || stringArrayContains(a.getString(index).split("\\|"), value); + } + + private static boolean matchTypedValue(TypedArray a, int index, int intValue, + String strValue) { + // If <case> does not have "index" attribute, that means this <case> is wild-card for + // the attribute. + final TypedValue v = a.peekValue(index); + if (v == null) + return true; + + if (isIntegerValue(v)) { + return intValue == a.getInt(index, 0); + } else if (isStringValue(v)) { + return stringArrayContains(a.getString(index).split("\\|"), strValue); + } + return false; + } + + private static boolean stringArrayContains(String[] array, String value) { + for (final String elem : array) { + if (elem.equals(value)) + return true; + } + return false; + } + + private boolean parseDefault(XmlPullParser parser, Row row, boolean skip) + throws XmlPullParserException, IOException { + if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT)); + if (row == null) { + parseKeyboardContent(parser, skip); + } else { + parseRowContent(parser, row, skip); + } + return true; + } + + private void parseKeyStyle(XmlPullParser parser, boolean skip) + throws XmlPullParserException { + TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_KeyStyle); + TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Key); + try { + if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) + throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE + + "/> needs styleName attribute", parser); + if (!skip) + mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); + } finally { + keyStyleAttr.recycle(); + keyAttrs.recycle(); + } + } + + private void startKeyboard() { + mCurrentY += mParams.mTopPadding; + mTopEdge = true; + } + + private void startRow(Row row) { + addEdgeSpace(mParams.mHorizontalEdgesPadding, row); + mCurrentRow = row; + mLeftEdge = true; + mRightEdgeKey = null; + } + + private void endRow(Row row) { + if (mCurrentRow == null) + throw new InflateException("orphant end row tag"); + if (mRightEdgeKey != null) { + mRightEdgeKey.markAsRightEdge(mParams); + mRightEdgeKey = null; + } + addEdgeSpace(mParams.mHorizontalEdgesPadding, row); + mCurrentY += row.mRowHeight; + mCurrentRow = null; + mTopEdge = false; + } + + private void endKey(Key key) { + mParams.onAddKey(key); + if (mLeftEdge) { + key.markAsLeftEdge(mParams); + mLeftEdge = false; + } + if (mTopEdge) { + key.markAsTopEdge(mParams); + } + mRightEdgeKey = key; + } + + private void endKeyboard() { + // nothing to do here. + } + + private void addEdgeSpace(float width, Row row) { + row.advanceXPos(width); + mLeftEdge = false; + mRightEdgeKey = null; + } + + public static float getDimensionOrFraction(TypedArray a, int index, int base, + float defValue) { + final TypedValue value = a.peekValue(index); + if (value == null) + return defValue; + if (isFractionValue(value)) { + return a.getFraction(index, base, base, defValue); + } else if (isDimensionValue(value)) { + return a.getDimension(index, defValue); + } + return defValue; + } + + public static int getEnumValue(TypedArray a, int index, int defValue) { + final TypedValue value = a.peekValue(index); + if (value == null) + return defValue; + if (isIntegerValue(value)) { + return a.getInt(index, defValue); + } + return defValue; + } + + private static boolean isFractionValue(TypedValue v) { + return v.type == TypedValue.TYPE_FRACTION; + } + + private static boolean isDimensionValue(TypedValue v) { + return v.type == TypedValue.TYPE_DIMENSION; + } + + private static boolean isIntegerValue(TypedValue v) { + return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT; + } + + private static boolean isStringValue(TypedValue v) { + return v.type == TypedValue.TYPE_STRING; + } + + private static String textAttr(String value, String name) { + return value != null ? String.format(" %s=%s", name, value) : ""; + } + + private static String booleanAttr(TypedArray a, int index, String name) { + return a.hasValue(index) + ? String.format(" %s=%s", name, a.getBoolean(index, false)) : ""; + } + } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 3b3ff0709..d95c3b3af 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -60,8 +60,6 @@ public class KeyboardId { public final int mOrientation; public final int mWidth; public final int mMode; - // TODO: Remove this field. - private final int mXmlId; public final int mElementState; private final int mInputType; private final int mImeOptions; @@ -72,23 +70,13 @@ public class KeyboardId { private final int mHashCode; - public KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width, - int mode, EditorInfo editorInfo, boolean settingsKeyEnabled, - boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { - this(xmlId, elementState, locale, orientation, width, mode, - (editorInfo != null ? editorInfo.inputType : 0), - (editorInfo != null ? editorInfo.imeOptions : 0), - settingsKeyEnabled, clobberSettingsKey, shortcutKeyEnabled, hasShortcutKey); - } - - private KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width, - int mode, int inputType, int imeOptions, boolean settingsKeyEnabled, - boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { + public KeyboardId(int elementState, Locale locale, int orientation, int width, int mode, + int inputType, int imeOptions, boolean settingsKeyEnabled, boolean clobberSettingsKey, + boolean shortcutKeyEnabled, boolean hasShortcutKey) { this.mLocale = locale; this.mOrientation = orientation; this.mWidth = width; this.mMode = mode; - this.mXmlId = xmlId; this.mElementState = elementState; this.mInputType = inputType; this.mImeOptions = imeOptions; @@ -106,7 +94,6 @@ public class KeyboardId { id.mElementState, id.mMode, id.mWidth, - id.mXmlId, id.navigateAction(), id.passwordInput(), id.mSettingsKeyEnabled, @@ -125,7 +112,6 @@ public class KeyboardId { && other.mElementState == this.mElementState && other.mMode == this.mMode && other.mWidth == this.mWidth - && other.mXmlId == this.mXmlId && other.navigateAction() == this.navigateAction() && other.passwordInput() == this.passwordInput() && other.mSettingsKeyEnabled == this.mSettingsKeyEnabled @@ -136,16 +122,6 @@ public class KeyboardId { && other.mLocale.equals(this.mLocale); } - public KeyboardId cloneWithNewXml(int xmlId) { - return new KeyboardId(xmlId, mElementState, mLocale, mOrientation, mWidth, mMode, - mInputType, mImeOptions, false, false, false, false); - } - - // Remove this method. - public int getXmlId() { - return mXmlId; - } - public boolean isAlphabetKeyboard() { return mElementState < ELEMENT_SYMBOLS; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index a28cfa85d..a2e784c99 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -17,94 +17,178 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.util.DisplayMetrics; +import android.util.Log; import android.util.Xml; import android.view.inputmethod.EditorInfo; -import com.android.inputmethod.keyboard.internal.XmlParseUtils; import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Locale; /** - * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard + * This class represents a set of keyboards. Each of them represents a different keyboard * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same * {@link KeyboardSet} are related to each other. * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}. */ public class KeyboardSet { - private static final String TAG_KEYBOARD_SET = "KeyboardSet"; + private static final String TAG = KeyboardSet.class.getSimpleName(); + private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG; + + private static final String TAG_KEYBOARD_SET = TAG; private static final String TAG_ELEMENT = "Element"; - // TODO: Make these KeyboardId private. - public final KeyboardId mAlphabetId; - public final KeyboardId mSymbolsId; - public final KeyboardId mSymbolsShiftedId; + private final Context mContext; + private final Params mParams; + + private static class Params { + int mMode; + int mInputType; + int mImeOptions; + boolean mSettingsKeyEnabled; + boolean mVoiceKeyEnabled; + boolean mVoiceKeyOnMain; + boolean mNoSettingsKey; + Locale mLocale; + int mOrientation; + int mWidth; + final HashMap<Integer, Integer> mElementKeyboards = new HashMap<Integer, Integer>(); + Params() {} + } + + private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache = + new HashMap<KeyboardId, SoftReference<Keyboard>>(); + + public static void clearKeyboardCache() { + sKeyboardCache.clear(); + } + + private KeyboardSet(Context context, Params params) { + mContext = context; + mParams = params; + } + + public Keyboard getMainKeyboard() { + return getKeyboard(false, false); + } + + public Keyboard getSymbolsKeyboard() { + return getKeyboard(true, false); + } + + public Keyboard getSymbolsShiftedKeyboard() { + final Keyboard keyboard = getKeyboard(true, true); + // TODO: Remove this logic once we introduce initial keyboard shift state attribute. + // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a. + // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked() + // that takes care of the current keyboard having such shift key or not. + keyboard.setShiftLocked(keyboard.hasShiftLockKey()); + return keyboard; + } - KeyboardSet(Builder builder) { - mAlphabetId = builder.getKeyboardId(false, false); - mSymbolsId = builder.getKeyboardId(true, false); - mSymbolsShiftedId = builder.getKeyboardId(true, true); + private Keyboard getKeyboard(boolean isSymbols, boolean isShift) { + final int elementState = Builder.getElementState(mParams.mMode, isSymbols, isShift); + final int xmlId = mParams.mElementKeyboards.get(elementState); + final KeyboardId id = Builder.getKeyboardId(elementState, isSymbols, mParams); + final Keyboard keyboard = getKeyboard(mContext, xmlId, id); + return keyboard; + } + + public KeyboardId getMainKeyboardId() { + final int elementState = Builder.getElementState(mParams.mMode, false, false); + return Builder.getKeyboardId(elementState, false, mParams); + } + + private static Keyboard getKeyboard(Context context, int xmlId, KeyboardId id) { + final Resources res = context.getResources(); + final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); + final SoftReference<Keyboard> ref = sKeyboardCache.get(id); + Keyboard keyboard = (ref == null) ? null : ref.get(); + if (keyboard == null) { + final Locale savedLocale = LocaleUtils.setSystemLocale(res, id.mLocale); + try { + final Keyboard.Builder<Keyboard.Params> builder = + new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params()); + builder.load(xmlId, id); + builder.setTouchPositionCorrectionEnabled( + subtypeSwitcher.currentSubtypeContainsExtraValueKey( + LatinIME.SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION)); + keyboard = builder.build(); + } finally { + LocaleUtils.setSystemLocale(res, savedLocale); + } + sKeyboardCache.put(id, new SoftReference<Keyboard>(keyboard)); + + if (DEBUG_CACHE) { + Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": " + + ((ref == null) ? "LOAD" : "GCed") + " id=" + id); + } + } else if (DEBUG_CACHE) { + Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": HIT id=" + id); + } + + // TODO: Remove setShiftLocked and setShift calls. + keyboard.setShiftLocked(false); + keyboard.setShifted(false); + return keyboard; } public static class Builder { + private final Context mContext; private final Resources mResources; - private final EditorInfo mEditorInfo; - - private final HashMap<Integer, Integer> mElementKeyboards = - new HashMap<Integer, Integer>(); - private final int mMode; - private final boolean mSettingsKeyEnabled; - private final boolean mVoiceKeyEnabled; - private final boolean mVoiceKeyOnMain; - private final boolean mNoSettingsKey; - private final Locale mLocale; - private final Configuration mConf; - private final DisplayMetrics mMetrics; + private final Params mParams = new Params(); public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) { + mContext = context; mResources = context.getResources(); - mEditorInfo = editorInfo; final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); final String packageName = context.getPackageName(); + final Params params = mParams; - mMode = Utils.getKeyboardMode(mEditorInfo); - mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); + params.mMode = Utils.getKeyboardMode(editorInfo); + if (editorInfo != null) { + params.mInputType = editorInfo.inputType; + params.mImeOptions = editorInfo.imeOptions; + } + params.mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); @SuppressWarnings("deprecation") final boolean noMicrophone = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo) || Utils.inPrivateImeOptions( null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo); - mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone; - mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); - mNoSettingsKey = Utils.inPrivateImeOptions( + params.mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone; + params.mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); + params.mNoSettingsKey = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo); final boolean forceAscii = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo); final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey( LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE); - mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale(); - mConf = mResources.getConfiguration(); - mMetrics = mResources.getDisplayMetrics(); + params.mLocale = (forceAscii && !asciiCapable) + ? Locale.US : subtypeSwitcher.getInputLocale(); + params.mOrientation = mResources.getConfiguration().orientation; + params.mWidth = mResources.getDisplayMetrics().widthPixels; } public KeyboardSet build() { - final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mLocale); + final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mParams.mLocale); try { parseKeyboardSet(mResources, R.xml.keyboard_set); } catch (Exception e) { @@ -112,19 +196,20 @@ public class KeyboardSet { } finally { LocaleUtils.setSystemLocale(mResources, savedLocale); } - return new KeyboardSet(this); + return new KeyboardSet(mContext, mParams); } - KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) { - final int elementState = getElementState(mMode, isSymbols, isShift); - final int xmlId = mElementKeyboards.get(elementState); - final boolean hasShortcutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain); - return new KeyboardId(xmlId, elementState, mLocale, mConf.orientation, - mMetrics.widthPixels, mMode, mEditorInfo, mSettingsKeyEnabled, mNoSettingsKey, - mVoiceKeyEnabled, hasShortcutKey); + // TODO: Move this method to KeyboardSet + static KeyboardId getKeyboardId(int elementState, boolean isSymbols, Params params) { + final boolean hasShortcutKey = params.mVoiceKeyEnabled + && (isSymbols != params.mVoiceKeyOnMain); + return new KeyboardId(elementState, params.mLocale, params.mOrientation, params.mWidth, + params.mMode, params.mInputType, params.mImeOptions, params.mSettingsKeyEnabled, + params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey); } - private static int getElementState(int mode, boolean isSymbols, boolean isShift) { + // TODO: Move this method to KeyboardSet + static int getElementState(int mode, boolean isSymbols, boolean isShift) { switch (mode) { case KeyboardId.MODE_PHONE: return (isSymbols && isShift) @@ -198,7 +283,7 @@ public class KeyboardSet { R.styleable.KeyboardSet_Element_elementName, 0); final int elementKeyboard = a.getResourceId( R.styleable.KeyboardSet_Element_elementKeyboard, 0); - mElementKeyboards.put(elementName, elementKeyboard); + mParams.mElementKeyboards.put(elementName, elementKeyboard); } finally { a.recycle(); } @@ -208,7 +293,8 @@ public class KeyboardSet { public static String parseKeyboardLocale(Resources res, int resId) throws XmlPullParserException, IOException { final XmlPullParser parser = res.getXml(resId); - if (parser == null) return ""; + if (parser == null) + return ""; int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 3d4a6efcd..e839fe7a3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -31,21 +31,15 @@ import com.android.inputmethod.keyboard.internal.KeyboardState; import com.android.inputmethod.latin.InputView; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; -import java.lang.ref.SoftReference; -import java.util.HashMap; -import java.util.Locale; - public class KeyboardSwitcher implements KeyboardState.SwitchActions, SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); - private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG; public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916"; private static final int[] KEYBOARD_THEMES = { @@ -69,9 +63,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, private KeyboardSet mKeyboardSet; - private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache = - new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); - /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of * what user actually typed. */ private boolean mIsAutoCorrectionActive; @@ -121,25 +112,27 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, if (mThemeIndex != themeIndex) { mThemeIndex = themeIndex; mThemeContext = new ContextThemeWrapper(context, KEYBOARD_THEMES[themeIndex]); - mKeyboardCache.clear(); + KeyboardSet.clearKeyboardCache(); } } public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) { + mKeyboardSet = new KeyboardSet.Builder(mThemeContext, editorInfo, settingsValues) + .build(); + final KeyboardId mainKeyboardId = mKeyboardSet.getMainKeyboardId(); try { - mKeyboardSet = new KeyboardSet.Builder(mInputMethodService, editorInfo, settingsValues) - .build(); mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols), hasDistinctMultitouch()); - // TODO: Should get rid of this special case handling for Phone Number layouts once we - // have separate layouts with unique KeyboardIds for alphabet and alphabet-shifted - // respectively. - if (mKeyboardSet.mAlphabetId.isPhoneKeyboard()) { - mState.onToggleAlphabetAndSymbols(); - } } catch (RuntimeException e) { - Log.w(TAG, "loading keyboard failed: " + mKeyboardSet.mAlphabetId, e); - LatinImeLogger.logOnException(mKeyboardSet.mAlphabetId.toString(), e); + Log.w(TAG, "loading keyboard failed: " + mainKeyboardId, e); + LatinImeLogger.logOnException(mainKeyboardId.toString(), e); + return; + } + // TODO: Should get rid of this special case handling for Phone Number layouts once we + // have separate layouts with unique KeyboardIds for alphabet and alphabet-shifted + // respectively. + if (mainKeyboardId.isPhoneKeyboard()) { + mState.onToggleAlphabetAndSymbols(); } } @@ -164,58 +157,21 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, mKeyboardView.setKeyPreviewPopupEnabled( SettingsValues.isKeyPreviewPopupEnabled(mPrefs, mResources), SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources)); + mKeyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive); + // If the cached keyboard had been switched to another keyboard while the language was + // displayed on its spacebar, it might have had arbitrary text fade factor. In such + // case, we should reset the text fade factor. It is also applicable to shortcut key. + mKeyboardView.updateSpacebar(0.0f, + mSubtypeSwitcher.needsToDisplayLanguage(keyboard.mId.mLocale)); + mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); + updateShiftState(); final boolean localeChanged = (oldKeyboard == null) || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); - if (keyboard instanceof LatinKeyboard) { - final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard; - latinKeyboard.updateAutoCorrectionState(mIsAutoCorrectionActive); - // If the cached keyboard had been switched to another keyboard while the language was - // displayed on its spacebar, it might have had arbitrary text fade factor. In such - // case, we should reset the text fade factor. It is also applicable to shortcut key. - latinKeyboard.updateSpacebarLanguage(0.0f, - Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */), - mSubtypeSwitcher.needsToDisplayLanguage(latinKeyboard.mId.mLocale)); - latinKeyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); - } - updateShiftState(); mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); } - // TODO: Move this method to KeyboardSet. - private LatinKeyboard getKeyboard(Context context, KeyboardId id) { - final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id); - LatinKeyboard keyboard = (ref == null) ? null : ref.get(); - if (keyboard == null) { - final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, id.mLocale); - try { - final LatinKeyboard.Builder builder = new LatinKeyboard.Builder(context); - builder.load(id); - builder.setTouchPositionCorrectionEnabled( - mSubtypeSwitcher.currentSubtypeContainsExtraValueKey( - LatinIME.SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION)); - keyboard = builder.build(); - } finally { - LocaleUtils.setSystemLocale(mResources, savedLocale); - } - mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard)); - - if (DEBUG_CACHE) { - Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " - + ((ref == null) ? "LOAD" : "GCed") + " id=" + id - + " theme=" + themeName(keyboard.mThemeId)); - } - } else if (DEBUG_CACHE) { - Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id - + " theme=" + themeName(keyboard.mThemeId)); - } - - keyboard.setShiftLocked(false); - keyboard.setShifted(false); - return keyboard; - } - public boolean isAlphabetMode() { - final Keyboard keyboard = getLatinKeyboard(); + final Keyboard keyboard = getKeyboard(); return keyboard != null && keyboard.mId.isAlphabetKeyboard(); } @@ -224,12 +180,12 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, } public boolean isShiftedOrShiftLocked() { - final Keyboard keyboard = getLatinKeyboard(); + final Keyboard keyboard = getKeyboard(); return keyboard != null && keyboard.isShiftedOrShiftLocked(); } public boolean isManualTemporaryUpperCase() { - final Keyboard keyboard = getLatinKeyboard(); + final Keyboard keyboard = getKeyboard(); return keyboard != null && keyboard.isManualTemporaryUpperCase(); } @@ -239,11 +195,9 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, return false; } - public LatinKeyboard getLatinKeyboard() { + public Keyboard getKeyboard() { if (mKeyboardView != null) { - final Keyboard keyboard = mKeyboardView.getKeyboard(); - if (keyboard instanceof LatinKeyboard) - return (LatinKeyboard)keyboard; + return mKeyboardView.getKeyboard(); } return null; } @@ -252,11 +206,11 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, @Override public void setShifted(int shiftMode) { mInputMethodService.mHandler.cancelUpdateShiftState(); - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard == null) + Keyboard keyboard = getKeyboard(); + if (keyboard == null) return; if (shiftMode == AUTOMATIC_SHIFT) { - latinKeyboard.setAutomaticTemporaryUpperCase(); + keyboard.setAutomaticTemporaryUpperCase(); } else { final boolean shifted = (shiftMode == MANUAL_SHIFT); // On non-distinct multi touch panel device, we should also turn off the shift locked @@ -264,9 +218,9 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, // On the other hand, on distinct multi touch panel device, turning off the shift // locked state with shift key pressing is handled by onReleaseShift(). if (!hasDistinctMultitouch() && !shifted && mState.isShiftLocked()) { - latinKeyboard.setShiftLocked(false); + keyboard.setShiftLocked(false); } - latinKeyboard.setShifted(shifted); + keyboard.setShifted(shifted); } mKeyboardView.invalidateAllKeys(); } @@ -275,10 +229,10 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, @Override public void setShiftLocked(boolean shiftLocked) { mInputMethodService.mHandler.cancelUpdateShiftState(); - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard == null) + Keyboard keyboard = getKeyboard(); + if (keyboard == null) return; - latinKeyboard.setShiftLocked(shiftLocked); + keyboard.setShiftLocked(shiftLocked); mKeyboardView.invalidateAllKeys(); if (!shiftLocked) { // To be able to turn off caps lock by "double tap" on shift key, we should ignore @@ -343,25 +297,19 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsKeyboard() { - setKeyboard(getKeyboard(mThemeContext, mKeyboardSet.mSymbolsId)); + setKeyboard(mKeyboardSet.getSymbolsKeyboard()); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetKeyboard() { - setKeyboard(getKeyboard(mThemeContext, mKeyboardSet.mAlphabetId)); + setKeyboard(mKeyboardSet.getMainKeyboard()); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsShiftedKeyboard() { - final Keyboard keyboard = getKeyboard(mThemeContext, mKeyboardSet.mSymbolsShiftedId); - setKeyboard(keyboard); - // TODO: Remove this logic once we introduce initial keyboard shift state attribute. - // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a. - // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked() - // that takes care of the current keyboard having such shift key or not. - keyboard.setShiftLocked(keyboard.hasShiftLockKey()); + setKeyboard(mKeyboardSet.getSymbolsShiftedKeyboard()); } public boolean isInMomentarySwitchState() { @@ -457,24 +405,16 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, } public void onNetworkStateChanged() { - final LatinKeyboard keyboard = getLatinKeyboard(); - if (keyboard == null) return; - final Key updatedKey = keyboard.updateShortcutKey( - SubtypeSwitcher.getInstance().isShortcutImeReady()); - if (updatedKey != null && mKeyboardView != null) { - mKeyboardView.invalidateKey(updatedKey); + if (mKeyboardView != null) { + mKeyboardView.updateShortcutKey(SubtypeSwitcher.getInstance().isShortcutImeReady()); } } public void onAutoCorrectionStateChanged(boolean isAutoCorrection) { if (mIsAutoCorrectionActive != isAutoCorrection) { mIsAutoCorrectionActive = isAutoCorrection; - final LatinKeyboard keyboard = getLatinKeyboard(); - if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) { - final Key invalidatedKey = keyboard.updateAutoCorrectionState(isAutoCorrection); - final LatinKeyboardView keyboardView = getKeyboardView(); - if (keyboardView != null) - keyboardView.invalidateKey(invalidatedKey); + if (mKeyboardView != null) { + mKeyboardView.updateAutoCorrectionState(isAutoCorrection); } } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index d2741edec..a174fd98f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -194,7 +194,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } - private static class KeyDrawParams { + /* package */ static class KeyDrawParams { // XML attributes public final int mKeyTextColor; public final int mKeyTextInactivatedColor; @@ -283,7 +283,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } - protected static class KeyPreviewDrawParams { + /* package */ static class KeyPreviewDrawParams { // XML attributes. public final Drawable mPreviewBackground; public final Drawable mPreviewLeftBackground; @@ -474,7 +474,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { if (mKeyboard == null) return; - final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase(); final KeyDrawParams params = mKeyDrawParams; if (mInvalidatedKey != null && mInvalidatedKeyRect.contains(mDirtyRect)) { // Draw a single key. @@ -482,8 +481,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { + getPaddingLeft(); final int keyDrawY = mInvalidatedKey.mY + getPaddingTop(); canvas.translate(keyDrawX, keyDrawY); - onBufferDrawKey(mInvalidatedKey, mKeyboard, canvas, mPaint, params, - isManualTemporaryUpperCase); + onDrawKey(mInvalidatedKey, canvas, mPaint, params); canvas.translate(-keyDrawX, -keyDrawY); } else { // Draw all keys. @@ -491,7 +489,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft(); final int keyDrawY = key.mY + getPaddingTop(); canvas.translate(keyDrawX, keyDrawY); - onBufferDrawKey(key, mKeyboard, canvas, mPaint, params, isManualTemporaryUpperCase); + onDrawKey(key, canvas, mPaint, params); canvas.translate(-keyDrawX, -keyDrawY); } } @@ -514,38 +512,43 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } - private static void onBufferDrawKey(final Key key, final Keyboard keyboard, final Canvas canvas, - Paint paint, KeyDrawParams params, boolean isManualTemporaryUpperCase) { - final boolean debugShowAlign = LatinImeLogger.sVISUALDEBUG; - // Draw key background. - if (!key.isSpacer()) { - final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight - + params.mPadding.left + params.mPadding.right; - final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom; - final int bgX = -params.mPadding.left; - final int bgY = -params.mPadding.top; - final int[] drawableState = key.getCurrentDrawableState(); - final Drawable background = params.mKeyBackground; - background.setState(drawableState); - final Rect bounds = background.getBounds(); - if (bgWidth != bounds.right || bgHeight != bounds.bottom) { - background.setBounds(0, 0, bgWidth, bgHeight); - } - canvas.translate(bgX, bgY); - background.draw(canvas); - if (debugShowAlign) { - drawRectangle(canvas, 0, 0, bgWidth, bgHeight, 0x80c00000, new Paint()); - } - canvas.translate(-bgX, -bgY); + private void onDrawKey(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { + if (key.isSpacer()) return; + onDrawKeyBackground(key, canvas, params); + onDrawKeyTopVisuals(key, canvas, paint, params); + } + + // Draw key background. + /* package */ void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) { + final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight + + params.mPadding.left + params.mPadding.right; + final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom; + final int bgX = -params.mPadding.left; + final int bgY = -params.mPadding.top; + final int[] drawableState = key.getCurrentDrawableState(); + final Drawable background = params.mKeyBackground; + background.setState(drawableState); + final Rect bounds = background.getBounds(); + if (bgWidth != bounds.right || bgHeight != bounds.bottom) { + background.setBounds(0, 0, bgWidth, bgHeight); } + canvas.translate(bgX, bgY); + background.draw(canvas); + if (LatinImeLogger.sVISUALDEBUG) { + drawRectangle(canvas, 0, 0, bgWidth, bgHeight, 0x80c00000, new Paint()); + } + canvas.translate(-bgX, -bgY); + } - // Draw key top visuals. + // Draw key top visuals. + /* package */ void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, + KeyDrawParams params) { final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; final int keyHeight = key.mHeight; final float centerX = keyWidth * 0.5f; final float centerY = keyHeight * 0.5f; - if (debugShowAlign) { + if (LatinImeLogger.sVISUALDEBUG) { drawRectangle(canvas, 0, 0, keyWidth, keyHeight, 0x800000c0, new Paint()); } @@ -554,7 +557,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { float positionX = centerX; if (key.mLabel != null) { // Switch the character to uppercase if shift is pressed - final CharSequence label = keyboard.adjustLabelCase(key.mLabel); + final CharSequence label = mKeyboard.adjustLabelCase(key.mLabel); // For characters, use large font. For labels like "Done", use smaller font. paint.setTypeface(key.selectTypeface(params.mKeyTextStyle)); final int labelSize = key.selectTextSize(params.mKeyLetterSize, @@ -597,7 +600,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint))); } - if (key.hasUppercaseLetter() && isManualTemporaryUpperCase) { + if (key.hasUppercaseLetter() && mKeyboard.isManualTemporaryUpperCase()) { paint.setColor(params.mKeyTextInactivatedColor); } else { paint.setColor(params.mKeyTextColor); @@ -627,7 +630,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } - if (debugShowAlign) { + if (LatinImeLogger.sVISUALDEBUG) { final Paint line = new Paint(); drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line); drawVerticalLine(canvas, positionX, keyHeight, 0xc0800080, line); @@ -644,7 +647,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { hintSize = params.mKeyHintLabelSize; paint.setTypeface(Typeface.DEFAULT); } else if (key.hasUppercaseLetter()) { - hintColor = isManualTemporaryUpperCase + hintColor = mKeyboard.isManualTemporaryUpperCase() ? params.mKeyUppercaseLetterActivatedColor : params.mKeyUppercaseLetterInactivatedColor; hintSize = params.mKeyUppercaseLetterSize; @@ -677,7 +680,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } canvas.drawText(hint, 0, hint.length(), hintX, hintY, paint); - if (debugShowAlign) { + if (LatinImeLogger.sVISUALDEBUG) { final Paint line = new Paint(); drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); @@ -702,29 +705,35 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); - if (debugShowAlign) { + if (LatinImeLogger.sVISUALDEBUG) { final Paint line = new Paint(); drawVerticalLine(canvas, alignX, keyHeight, 0xc0800080, line); drawRectangle(canvas, iconX, iconY, iconWidth, iconHeight, 0x80c00000, line); } } - // Draw popup hint "..." at the bottom right corner of the key. - if ((key.hasPopupHint() && key.mMoreKeys != null && key.mMoreKeys.length > 0) - || key.needsSpecialPopupHint()) { - paint.setTextSize(params.mKeyHintLetterSize); - paint.setColor(params.mKeyHintLabelColor); - paint.setTextAlign(Align.CENTER); - final float hintX = keyWidth - params.mKeyHintLetterPadding - - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; - final float hintY = keyHeight - params.mKeyPopupHintLetterPadding; - canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); + if (key.hasPopupHint() && key.mMoreKeys != null && key.mMoreKeys.length > 0) { + drawKeyPopupHint(key, canvas, paint, params); + } + } + + // Draw popup hint "..." at the bottom right corner of the key. + /* package */ void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { + final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; + final int keyHeight = key.mHeight; - if (debugShowAlign) { - final Paint line = new Paint(); - drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); - drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); - } + paint.setTextSize(params.mKeyHintLetterSize); + paint.setColor(params.mKeyHintLabelColor); + paint.setTextAlign(Align.CENTER); + final float hintX = keyWidth - params.mKeyHintLetterPadding + - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; + final float hintY = keyHeight - params.mKeyPopupHintLetterPadding; + canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); + + if (LatinImeLogger.sVISUALDEBUG) { + final Paint line = new Paint(); + drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); + drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); } } @@ -889,7 +898,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } previewText.setText(mKeyboard.adjustLabelCase(key.mLabel)); } else { - final Drawable previewIcon = key.getPreviewIcon(); + final Drawable previewIcon = key.mPreviewIcon; previewText.setCompoundDrawables(null, null, null, previewIcon != null ? previewIcon : key.getIcon()); previewText.setText(null); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java deleted file mode 100644 index abb96f0bb..000000000 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.keyboard; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.Resources.Theme; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; - -import com.android.inputmethod.keyboard.internal.KeyboardBuilder; -import com.android.inputmethod.keyboard.internal.KeyboardParams; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.Utils; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Locale; - -// TODO: We should remove this class -public class LatinKeyboard extends Keyboard { - private static final int SPACE_LED_LENGTH_PERCENT = 80; - - private final Resources mRes; - private final Theme mTheme; - - /* Space key and its icons, drawables and colors. */ - private final Key mSpaceKey; - private final Drawable mSpaceIcon; - private final boolean mAutoCorrectionSpacebarLedEnabled; - private final Drawable mAutoCorrectionSpacebarLedIcon; - private final int mSpacebarTextColor; - private final int mSpacebarTextShadowColor; - private final HashMap<Integer, BitmapDrawable> mSpaceDrawableCache = - new HashMap<Integer, BitmapDrawable>(); - private final boolean mIsSpacebarTriggeringPopupByLongPress; - - private boolean mAutoCorrectionSpacebarLedOn; - private boolean mMultipleEnabledIMEsOrSubtypes; - private boolean mNeedsToDisplayLanguage; - private float mSpacebarTextFadeFactor = 0.0f; - - /* Shortcut key and its icons if available */ - private final Key mShortcutKey; - private final Drawable mEnabledShortcutIcon; - private final Drawable mDisabledShortcutIcon; - - // Height in space key the language name will be drawn. (proportional to space key height) - public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; - // If the full language name needs to be smaller than this value to be drawn on space key, - // its short language name will be used instead. - private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f; - - private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small"; - private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium"; - - private LatinKeyboard(Context context, LatinKeyboardParams params) { - super(params); - mRes = context.getResources(); - mTheme = context.getTheme(); - - // The index of space key is available only after Keyboard constructor has finished. - mSpaceKey = params.mSpaceKey; - mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null; - - mShortcutKey = params.mShortcutKey; - mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null; - final int longPressSpaceKeyTimeout = - mRes.getInteger(R.integer.config_long_press_space_key_timeout); - mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0); - - final TypedArray a = context.obtainStyledAttributes( - null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard); - mAutoCorrectionSpacebarLedEnabled = a.getBoolean( - R.styleable.LatinKeyboard_autoCorrectionSpacebarLedEnabled, false); - mAutoCorrectionSpacebarLedIcon = a.getDrawable( - R.styleable.LatinKeyboard_autoCorrectionSpacebarLedIcon); - mDisabledShortcutIcon = a.getDrawable(R.styleable.LatinKeyboard_disabledShortcutIcon); - mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0); - mSpacebarTextShadowColor = a.getColor( - R.styleable.LatinKeyboard_spacebarTextShadowColor, 0); - a.recycle(); - } - - private static class LatinKeyboardParams extends KeyboardParams { - public Key mSpaceKey = null; - public Key mShortcutKey = null; - - @Override - public void onAddKey(Key key) { - super.onAddKey(key); - - switch (key.mCode) { - case Keyboard.CODE_SPACE: - mSpaceKey = key; - break; - case Keyboard.CODE_SHORTCUT: - mShortcutKey = key; - break; - } - } - } - - public static class Builder extends KeyboardBuilder<LatinKeyboardParams> { - public Builder(Context context) { - super(context, new LatinKeyboardParams()); - } - - @Override - public Builder load(KeyboardId id) { - super.load(id); - return this; - } - - @Override - public LatinKeyboard build() { - return new LatinKeyboard(mContext, mParams); - } - } - - public Key updateSpacebarLanguage(float fadeFactor, boolean multipleEnabledIMEsOrSubtypes, - boolean needsToDisplayLanguage) { - mSpacebarTextFadeFactor = fadeFactor; - mMultipleEnabledIMEsOrSubtypes = multipleEnabledIMEsOrSubtypes; - mNeedsToDisplayLanguage = needsToDisplayLanguage; - updateSpacebarIcon(); - return mSpaceKey; - } - - private static int getSpacebarTextColor(int color, float fadeFactor) { - final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor), - Color.red(color), Color.green(color), Color.blue(color)); - return newColor; - } - - public Key updateShortcutKey(boolean available) { - if (mShortcutKey == null) - return null; - mShortcutKey.setEnabled(available); - mShortcutKey.setIcon(available ? mEnabledShortcutIcon : mDisabledShortcutIcon); - return mShortcutKey; - } - - public boolean needsAutoCorrectionSpacebarLed() { - return mAutoCorrectionSpacebarLedEnabled; - } - - /** - * @return a key which should be invalidated. - */ - public Key updateAutoCorrectionState(boolean isAutoCorrection) { - mAutoCorrectionSpacebarLedOn = isAutoCorrection; - updateSpacebarIcon(); - return mSpaceKey; - } - - @Override - public CharSequence adjustLabelCase(CharSequence label) { - if (mId.isAlphabetKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) - && label.length() < 3 && Character.isLowerCase(label.charAt(0))) { - return label.toString().toUpperCase(mId.mLocale); - } - return label; - } - - private void updateSpacebarIcon() { - if (mSpaceKey == null) return; - final boolean shouldShowInputMethodPicker = mIsSpacebarTriggeringPopupByLongPress - && mMultipleEnabledIMEsOrSubtypes; - mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker); - if (mNeedsToDisplayLanguage) { - mSpaceKey.setIcon(getSpaceDrawable(mId.mLocale)); - } else if (mAutoCorrectionSpacebarLedOn) { - mSpaceKey.setIcon(getSpaceDrawable(null)); - } else { - mSpaceKey.setIcon(mSpaceIcon); - } - } - - // Compute width of text with specified text size using paint. - private static int getTextWidth(Paint paint, String text, float textSize, Rect bounds) { - paint.setTextSize(textSize); - paint.getTextBounds(text, 0, text.length(), bounds); - return bounds.width(); - } - - // Layout local language name and left and right arrow on spacebar. - private static String layoutSpacebar(Paint paint, Locale locale, int width, - float origTextSize) { - final Rect bounds = new Rect(); - - // Estimate appropriate language name text size to fit in maxTextWidth. - String language = Utils.getFullDisplayName(locale, true); - int textWidth = getTextWidth(paint, language, origTextSize, bounds); - // Assuming text width and text size are proportional to each other. - float textSize = origTextSize * Math.min(width / textWidth, 1.0f); - // allow variable text size - textWidth = getTextWidth(paint, language, textSize, bounds); - // If text size goes too small or text does not fit, use middle or short name - final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) - || (textWidth > width); - - final boolean useShortName; - if (useMiddleName) { - language = Utils.getMiddleDisplayLanguage(locale); - textWidth = getTextWidth(paint, language, origTextSize, bounds); - textSize = origTextSize * Math.min(width / textWidth, 1.0f); - useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) - || (textWidth > width); - } else { - useShortName = false; - } - - if (useShortName) { - language = Utils.getShortDisplayLanguage(locale); - textWidth = getTextWidth(paint, language, origTextSize, bounds); - textSize = origTextSize * Math.min(width / textWidth, 1.0f); - } - paint.setTextSize(textSize); - - return language; - } - - private BitmapDrawable getSpaceDrawable(Locale locale) { - final Integer hashCode = Arrays.hashCode( - new Object[] { locale, mAutoCorrectionSpacebarLedOn, mSpacebarTextFadeFactor }); - final BitmapDrawable cached = mSpaceDrawableCache.get(hashCode); - if (cached != null) { - return cached; - } - final BitmapDrawable drawable = new BitmapDrawable(mRes, drawSpacebar( - locale, mAutoCorrectionSpacebarLedOn, mSpacebarTextFadeFactor)); - mSpaceDrawableCache.put(hashCode, drawable); - return drawable; - } - - private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection, - float textFadeFactor) { - final int width = mSpaceKey.mWidth; - final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight; - final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(buffer); - final Resources res = mRes; - canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - - // If application locales are explicitly selected. - if (inputLocale != null) { - final Paint paint = new Paint(); - paint.setAntiAlias(true); - paint.setTextAlign(Align.CENTER); - - final String textSizeOfLanguageOnSpacebar = res.getString( - R.string.config_text_size_of_language_on_spacebar, - SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR); - final int textStyle; - final int defaultTextSize; - if (MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR.equals(textSizeOfLanguageOnSpacebar)) { - textStyle = android.R.style.TextAppearance_Medium; - defaultTextSize = 18; - } else { - textStyle = android.R.style.TextAppearance_Small; - defaultTextSize = 14; - } - - final String language = layoutSpacebar(paint, inputLocale, width, getTextSizeFromTheme( - mTheme, textStyle, defaultTextSize)); - - // Draw language text with shadow - // In case there is no space icon, we will place the language text at the center of - // spacebar. - final float descent = paint.descent(); - final float textHeight = -paint.ascent() + descent; - final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE - : height / 2 + textHeight / 2; - paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor)); - canvas.drawText(language, width / 2, baseline - descent - 1, paint); - paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor)); - canvas.drawText(language, width / 2, baseline - descent, paint); - } - - // Draw the spacebar icon at the bottom - if (isAutoCorrection) { - final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; - final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); - int x = (width - iconWidth) / 2; - int y = height - iconHeight; - mAutoCorrectionSpacebarLedIcon.setBounds(x, y, x + iconWidth, y + iconHeight); - mAutoCorrectionSpacebarLedIcon.draw(canvas); - } else if (mSpaceIcon != null) { - final int iconWidth = mSpaceIcon.getIntrinsicWidth(); - final int iconHeight = mSpaceIcon.getIntrinsicHeight(); - int x = (width - iconWidth) / 2; - int y = height - iconHeight; - mSpaceIcon.setBounds(x, y, x + iconWidth, y + iconHeight); - mSpaceIcon.draw(canvas); - } - return buffer; - } - - @Override - public int[] getNearestKeys(int x, int y) { - // Avoid dead pixels at edges of the keyboard - return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)), - Math.max(0, Math.min(y, mOccupiedHeight - 1))); - } - - private static final int[] ATTR_TEXT_SIZE = { android.R.attr.textSize }; - - public static int getTextSizeFromTheme(Theme theme, int style, int defValue) { - final TypedArray a = theme.obtainStyledAttributes(style, ATTR_TEXT_SIZE); - final int textSize = a.getDimensionPixelSize(a.getResourceId(0, 0), defValue); - a.recycle(); - return textSize; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 376f4c7c3..3433cd455 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -19,8 +19,18 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Message; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -38,10 +48,15 @@ import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; import java.util.WeakHashMap; /** @@ -57,11 +72,39 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true; + /* Space key and its icons, drawables and colors. */ + private Key mSpaceKey; + private Drawable mSpaceIcon; + private final boolean mIsSpacebarTriggeringPopupByLongPress; + private static final int SPACE_LED_LENGTH_PERCENT = 80; + private final boolean mAutoCorrectionSpacebarLedEnabled; + private final Drawable mAutoCorrectionSpacebarLedIcon; + private final float mSpacebarTextRatio; + private float mSpacebarTextSize; + private final int mSpacebarTextColor; + private final int mSpacebarTextShadowColor; + private final HashMap<Integer, BitmapDrawable> mSpacebarDrawableCache = + new HashMap<Integer, BitmapDrawable>(); + + private boolean mAutoCorrectionSpacebarLedOn; + private boolean mNeedsToDisplayLanguage; + private Locale mSpacebarLocale; + private float mSpacebarTextFadeFactor = 0.0f; + + // Height in space key the language name will be drawn. (proportional to space key height) + public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; + // If the full language name needs to be smaller than this value to be drawn on space key, + // its short language name will be used instead. + private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f; + private final SuddenJumpingTouchEventHandler mTouchScreenRegulator; // Timing constants private final int mKeyRepeatInterval; + // TODO: Kill process when the usability study mode was changed. + private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy; + // Mini keyboard private PopupWindow mMoreKeysWindow; private MoreKeysPanel mMoreKeysPanel; @@ -176,8 +219,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke @Override public boolean onDoubleTap(MotionEvent firstDown) { final Keyboard keyboard = getKeyboard(); - if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard instanceof LatinKeyboard - && ((LatinKeyboard) keyboard).mId.isAlphabetKeyboard()) { + if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard.mId.isAlphabetKeyboard()) { final int pointerIndex = firstDown.getActionIndex(); final int id = firstDown.getPointerId(pointerIndex); final PointerTracker tracker = getPointerTracker(id); @@ -206,7 +248,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // Detected a double tap on shift key. If we are in the ignoring double tap // mode, it means we have already turned off caps lock in // {@link KeyboardSwitcher#onReleaseShift} . - onDoubleTapShiftKey(tracker, mKeyTimerHandler.isIgnoringDoubleTap()); + onDoubleTapShiftKey(mKeyTimerHandler.isIgnoringDoubleTap()); return true; } // Otherwise these events should not be handled as double tap. @@ -217,7 +259,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke } public LatinKeyboardView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.keyboardViewStyle); + this(context, attrs, R.attr.latinKeyboardViewStyle); } public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) { @@ -241,6 +283,23 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); PointerTracker.init(mHasDistinctMultitouch, getContext()); + + final int longPressSpaceKeyTimeout = + res.getInteger(R.integer.config_long_press_space_key_timeout); + mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0); + + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView); + mAutoCorrectionSpacebarLedEnabled = a.getBoolean( + R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false); + mAutoCorrectionSpacebarLedIcon = a.getDrawable( + R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon); + mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio, + 1000, 1000, 1) / 1000.0f; + mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0); + mSpacebarTextShadowColor = a.getColor( + R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0); + a.recycle(); } public void startIgnoringDoubleTap() { @@ -295,6 +354,13 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke PointerTracker.setKeyDetector(mKeyDetector); mTouchScreenRegulator.setKeyboard(keyboard); mMoreKeysPanelCache.clear(); + + mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE); + mSpaceIcon = keyboard.mIconsSet.getIconByAttrId(R.styleable.Keyboard_iconSpaceKey); + final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; + mSpacebarTextSize = keyHeight * mSpacebarTextRatio; + mSpacebarLocale = keyboard.mId.mLocale; + clearSpacebarDrawableCache(); } /** @@ -342,8 +408,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke return onLongPress(parentKey, tracker); } - private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker, - final boolean ignore) { + private void onDoubleTapShiftKey(final boolean ignore) { // When shift key is double tapped, the first tap is correctly processed as usual tap. And // the second tap is treated as this double tap event, so that we need not mark tracker // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue. @@ -385,29 +450,26 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke protected boolean onLongPress(Key parentKey, PointerTracker tracker) { final int primaryCode = parentKey.mCode; final Keyboard keyboard = getKeyboard(); - if (keyboard instanceof LatinKeyboard) { - final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard; - if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.mId.isPhoneKeyboard()) { - tracker.onLongPressed(); - // Long pressing on 0 in phone number keypad gives you a '+'. - invokeCodeInput(Keyboard.CODE_PLUS); - invokeReleaseKey(primaryCode); - return true; - } - if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.mId.isAlphabetKeyboard()) { + if (primaryCode == Keyboard.CODE_DIGIT0 && keyboard.mId.isPhoneKeyboard()) { + tracker.onLongPressed(); + // Long pressing on 0 in phone number keypad gives you a '+'. + invokeCodeInput(Keyboard.CODE_PLUS); + invokeReleaseKey(primaryCode); + return true; + } + if (primaryCode == Keyboard.CODE_SHIFT && keyboard.mId.isAlphabetKeyboard()) { + tracker.onLongPressed(); + invokeCodeInput(Keyboard.CODE_CAPSLOCK); + invokeReleaseKey(primaryCode); + return true; + } + if (primaryCode == Keyboard.CODE_SPACE) { + // Long pressing the space key invokes IME switcher dialog. + if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { tracker.onLongPressed(); - invokeCodeInput(Keyboard.CODE_CAPSLOCK); invokeReleaseKey(primaryCode); return true; } - if (primaryCode == Keyboard.CODE_SPACE) { - // Long pressing the space key invokes IME switcher dialog. - if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { - tracker.onLongPressed(); - invokeReleaseKey(primaryCode); - return true; - } - } } return openMoreKeysPanel(parentKey, tracker); } @@ -514,6 +576,33 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke 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)) { + UsabilityStudyLogUtils.getInstance().write( + eventTag + eventTime + "," + id + "," + x + "," + y + "\t\t"); + } + } if (mKeyTimerHandler.isInKeyRepeat()) { final PointerTracker tracker = getPointerTracker(id); @@ -570,6 +659,10 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke py = (int)me.getY(i); } tracker.onMoveEvent(px, py, eventTime); + if (ENABLE_USABILITY_STUDY_LOG) { + UsabilityStudyLogUtils.getInstance().write("[Move]" + eventTime + "," + + me.getPointerId(i) + "," + px + "," + py + "\t\t"); + } } } else { getPointerTracker(id).processMotionEvent(action, x, y, eventTime, this); @@ -624,9 +717,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - final PointerTracker tracker = getPointerTracker(0); return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent( - event, tracker) || super.dispatchPopulateAccessibilityEvent(event); + event) || super.dispatchPopulateAccessibilityEvent(event); } return super.dispatchPopulateAccessibilityEvent(event); @@ -650,4 +742,180 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // Reflection doesn't support calling superclass methods. return false; } + + public void updateShortcutKey(boolean available) { + final Keyboard keyboard = getKeyboard(); + if (keyboard == null) return; + final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT); + if (shortcutKey == null) return; + shortcutKey.setEnabled(available); + invalidateKey(shortcutKey); + } + + public void updateSpacebar(float fadeFactor, boolean needsToDisplayLanguage) { + mSpacebarTextFadeFactor = fadeFactor; + mNeedsToDisplayLanguage = needsToDisplayLanguage; + updateSpacebarIcon(); + invalidateKey(mSpaceKey); + } + + public void updateAutoCorrectionState(boolean isAutoCorrection) { + if (!mAutoCorrectionSpacebarLedEnabled) return; + mAutoCorrectionSpacebarLedOn = isAutoCorrection; + updateSpacebarIcon(); + invalidateKey(mSpaceKey); + } + + @Override + /* package */ void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, + KeyDrawParams params) { + super.onDrawKeyTopVisuals(key, canvas, paint, params); + + if (key.mCode == Keyboard.CODE_SPACE) { + // Whether space key needs to show the "..." popup hint for special purposes + if (mIsSpacebarTriggeringPopupByLongPress + && Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { + super.drawKeyPopupHint(key, canvas, paint, params); + } + } + } + + // TODO: Get rid of this method and draw spacebar locale and auto correction spacebar LED + // in onDrawKeyTopVisuals. + private void updateSpacebarIcon() { + if (mSpaceKey == null) return; + if (mNeedsToDisplayLanguage) { + mSpaceKey.setIcon(getSpaceDrawable(mSpacebarLocale)); + } else if (mAutoCorrectionSpacebarLedOn) { + mSpaceKey.setIcon(getSpaceDrawable(null)); + } else { + mSpaceKey.setIcon(mSpaceIcon); + } + } + + private static int getSpacebarTextColor(int color, float fadeFactor) { + final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor), + Color.red(color), Color.green(color), Color.blue(color)); + return newColor; + } + + // Compute width of text with specified text size using paint. + private static int getTextWidth(Paint paint, String text, float textSize, Rect bounds) { + paint.setTextSize(textSize); + paint.getTextBounds(text, 0, text.length(), bounds); + return bounds.width(); + } + + // Layout local language name and left and right arrow on spacebar. + private static String layoutSpacebar(Paint paint, Locale locale, int width, + float origTextSize) { + final Rect bounds = new Rect(); + + // Estimate appropriate language name text size to fit in maxTextWidth. + String language = Utils.getFullDisplayName(locale, true); + int textWidth = getTextWidth(paint, language, origTextSize, bounds); + // Assuming text width and text size are proportional to each other. + float textSize = origTextSize * Math.min(width / textWidth, 1.0f); + // allow variable text size + textWidth = getTextWidth(paint, language, textSize, bounds); + // If text size goes too small or text does not fit, use middle or short name + final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) + || (textWidth > width); + + final boolean useShortName; + if (useMiddleName) { + language = Utils.getMiddleDisplayLanguage(locale); + textWidth = getTextWidth(paint, language, origTextSize, bounds); + textSize = origTextSize * Math.min(width / textWidth, 1.0f); + useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) + || (textWidth > width); + } else { + useShortName = false; + } + + if (useShortName) { + language = Utils.getShortDisplayLanguage(locale); + textWidth = getTextWidth(paint, language, origTextSize, bounds); + textSize = origTextSize * Math.min(width / textWidth, 1.0f); + } + paint.setTextSize(textSize); + + return language; + } + + private Integer getSpaceDrawableKey(Locale locale) { + return Arrays.hashCode(new Object[] { + locale, + mAutoCorrectionSpacebarLedOn, + mSpacebarTextFadeFactor + }); + } + + private void clearSpacebarDrawableCache() { + for (final BitmapDrawable drawable : mSpacebarDrawableCache.values()) { + final Bitmap bitmap = drawable.getBitmap(); + bitmap.recycle(); + } + mSpacebarDrawableCache.clear(); + } + + private BitmapDrawable getSpaceDrawable(Locale locale) { + final Integer hashCode = getSpaceDrawableKey(locale); + final BitmapDrawable cached = mSpacebarDrawableCache.get(hashCode); + if (cached != null) { + return cached; + } + final BitmapDrawable drawable = new BitmapDrawable(getResources(), drawSpacebar( + locale, mAutoCorrectionSpacebarLedOn, mSpacebarTextFadeFactor)); + mSpacebarDrawableCache.put(hashCode, drawable); + return drawable; + } + + private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection, + float textFadeFactor) { + final int width = mSpaceKey.mWidth; + final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight; + final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(buffer); + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + + // If application locales are explicitly selected. + if (inputLocale != null) { + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextAlign(Align.CENTER); + + final String language = layoutSpacebar(paint, inputLocale, width, mSpacebarTextSize); + + // Draw language text with shadow + // In case there is no space icon, we will place the language text at the center of + // spacebar. + final float descent = paint.descent(); + final float textHeight = -paint.ascent() + descent; + final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE + : height / 2 + textHeight / 2; + paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor)); + canvas.drawText(language, width / 2, baseline - descent - 1, paint); + paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor)); + canvas.drawText(language, width / 2, baseline - descent, paint); + } + + // Draw the spacebar icon at the bottom + if (isAutoCorrection) { + final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; + final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); + int x = (width - iconWidth) / 2; + int y = height - iconHeight; + mAutoCorrectionSpacebarLedIcon.setBounds(x, y, x + iconWidth, y + iconHeight); + mAutoCorrectionSpacebarLedIcon.draw(canvas); + } else if (mSpaceIcon != null) { + final int iconWidth = mSpaceIcon.getIntrinsicWidth(); + final int iconHeight = mSpaceIcon.getIntrinsicHeight(); + int x = (width - iconWidth) / 2; + int y = height - iconHeight; + mSpaceIcon.setBounds(x, y, x + iconWidth, y + iconHeight); + mSpaceIcon.draw(canvas); + } + return buffer; + } } diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index e0f21a247..548b5ea85 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -18,8 +18,6 @@ package com.android.inputmethod.keyboard; import android.graphics.Paint; -import com.android.inputmethod.keyboard.internal.KeyboardBuilder; -import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; import com.android.inputmethod.latin.R; @@ -35,10 +33,10 @@ public class MiniKeyboard extends Keyboard { return mDefaultKeyCoordX; } - public static class Builder extends KeyboardBuilder<Builder.MiniKeyboardParams> { + public static class Builder extends Keyboard.Builder<Builder.MiniKeyboardParams> { private final CharSequence[] mMoreKeys; - public static class MiniKeyboardParams extends KeyboardParams { + public static class MiniKeyboardParams extends Keyboard.Params { /* package */int mTopRowAdjustment; public int mNumRows; public int mNumColumns; @@ -207,7 +205,7 @@ public class MiniKeyboard extends Keyboard { public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) { super(view.getContext(), new MiniKeyboardParams()); - load(parentKeyboard.mId.cloneWithNewXml(xmlId)); + load(xmlId, parentKeyboard.mId); // TODO: Mini keyboard's vertical gap is currently calculated heuristically. // Should revise the algorithm. diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 3a07cdf4d..7c14b5888 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -29,6 +29,7 @@ import com.android.inputmethod.latin.R; import java.util.ArrayList; import java.util.List; +import java.util.Set; public class PointerTracker { private static final String TAG = PointerTracker.class.getSimpleName(); @@ -118,7 +119,7 @@ public class PointerTracker { private KeyboardActionListener mListener = EMPTY_LISTENER; private Keyboard mKeyboard; - private List<Key> mKeys; + private Set<Key> mKeys; private int mKeyQuarterWidthSquared; private final TextView mKeyPreviewText; diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 778aac3de..c1dae0601 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -18,19 +18,19 @@ package com.android.inputmethod.keyboard; import android.graphics.Rect; -import com.android.inputmethod.keyboard.internal.KeyboardParams.TouchPositionCorrection; +import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; import java.util.Arrays; import java.util.Collections; -import java.util.List; +import java.util.Set; public class ProximityInfo { 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 int[] EMPTY_INT_ARRAY = new int[0]; + private static final Key[] EMPTY_KEY_ARRAY = new Key[0]; private final int mKeyHeight; private final int mGridWidth; @@ -41,10 +41,10 @@ public class ProximityInfo { // TODO: Find a proper name for mKeyboardMinWidth private final int mKeyboardMinWidth; private final int mKeyboardHeight; - private final int[][] mGridNeighbors; + private final Key[][] mGridNeighbors; ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, - int keyHeight, List<Key> keys, TouchPositionCorrection touchPositionCorrection) { + int keyHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection) { mGridWidth = gridWidth; mGridHeight = gridHeight; mGridSize = mGridWidth * mGridHeight; @@ -53,7 +53,7 @@ public class ProximityInfo { mKeyboardMinWidth = minWidth; mKeyboardHeight = height; mKeyHeight = keyHeight; - mGridNeighbors = new int[mGridSize][]; + mGridNeighbors = new Key[mGridSize][]; if (minWidth == 0 || height == 0) { // No proximity required. Keyboard might be mini keyboard. return; @@ -62,7 +62,7 @@ public class ProximityInfo { } public static ProximityInfo createDummyProximityInfo() { - return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptyList(), null); + return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptySet(), null); } public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity) { @@ -86,16 +86,16 @@ public class ProximityInfo { float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii); private native void releaseProximityInfoNative(long nativeProximityInfo); - private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth, - int keyboardHeight, List<Key> keys, + private final void setProximityInfo(Key[][] gridNeighborKeys, int keyboardWidth, + int keyboardHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection) { - int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; + final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE); for (int i = 0; i < mGridSize; ++i) { - final int proximityCharsLength = gridNeighborKeyIndexes[i].length; + final int proximityCharsLength = gridNeighborKeys[i].length; for (int j = 0; j < proximityCharsLength; ++j) { proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] = - keys.get(gridNeighborKeyIndexes[i][j]).mCode; + gridNeighborKeys[i][j].mCode; } } final int keyCount = keys.size(); @@ -104,25 +104,45 @@ public class ProximityInfo { final int[] keyWidths = new int[keyCount]; final int[] keyHeights = new int[keyCount]; final int[] keyCharCodes = new int[keyCount]; - for (int i = 0; i < keyCount; ++i) { - final Key key = keys.get(i); + final float[] sweetSpotCenterXs; + final float[] sweetSpotCenterYs; + final float[] sweetSpotRadii; + final boolean calculateSweetSpotParams; + if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { + sweetSpotCenterXs = new float[keyCount]; + sweetSpotCenterYs = new float[keyCount]; + sweetSpotRadii = new float[keyCount]; + calculateSweetSpotParams = true; + } else { + sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null; + calculateSweetSpotParams = false; + } + + int i = 0; + for (final Key key : keys) { keyXCoordinates[i] = key.mX; keyYCoordinates[i] = key.mY; keyWidths[i] = key.mWidth; keyHeights[i] = key.mHeight; keyCharCodes[i] = key.mCode; - } - - float[] sweetSpotCenterXs = null; - float[] sweetSpotCenterYs = null; - float[] sweetSpotRadii = null; - - if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { - sweetSpotCenterXs = new float[keyCount]; - sweetSpotCenterYs = new float[keyCount]; - sweetSpotRadii = new float[keyCount]; - calculateSweetSpot(keys, touchPositionCorrection, - sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); + if (calculateSweetSpotParams) { + final Rect hitBox = key.mHitBox; + final int row = hitBox.top / mKeyHeight; + if (row < touchPositionCorrection.mRadii.length) { + final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f; + final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f; + final float hitBoxWidth = hitBox.right - hitBox.left; + final float hitBoxHeight = hitBox.bottom - hitBox.top; + final float x = touchPositionCorrection.mXs[row]; + final float y = touchPositionCorrection.mYs[row]; + final float radius = touchPositionCorrection.mRadii[row]; + sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; + sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; + sweetSpotRadii[i] = radius * (float)Math.sqrt( + hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); + } + } + i++; } mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE, @@ -131,32 +151,6 @@ public class ProximityInfo { sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); } - private void calculateSweetSpot(List<Key> keys, TouchPositionCorrection touchPositionCorrection, - float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii) { - final int keyCount = keys.size(); - final float[] xs = touchPositionCorrection.mXs; - final float[] ys = touchPositionCorrection.mYs; - final float[] radii = touchPositionCorrection.mRadii; - for (int i = 0; i < keyCount; ++i) { - final Key key = keys.get(i); - final Rect hitBox = key.mHitBox; - final int row = hitBox.top / mKeyHeight; - if (row < radii.length) { - final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f; - final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f; - final float hitBoxWidth = hitBox.right - hitBox.left; - final float hitBoxHeight = hitBox.bottom - hitBox.top; - final float x = xs[row]; - final float y = ys[row]; - final float radius = radii[row]; - sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; - sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; - sweetSpotRadii[i] = radius - * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); - } - } - } - public long getNativeProximityInfo() { return mNativeProximityInfo; } @@ -173,12 +167,12 @@ public class ProximityInfo { } } - private void computeNearestNeighbors(int defaultWidth, List<Key> keys, + private void computeNearestNeighbors(int defaultWidth, Set<Key> keys, TouchPositionCorrection touchPositionCorrection) { final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE); final int threshold = thresholdBase * thresholdBase; // Round-up so we don't have any pixels outside the grid - final int[] indices = new int[keys.size()]; + final Key[] neighborKeys = new Key[keys.size()]; final int gridWidth = mGridWidth * mCellWidth; final int gridHeight = mGridHeight * mCellHeight; for (int x = 0; x < gridWidth; x += mCellWidth) { @@ -186,24 +180,23 @@ public class ProximityInfo { final int centerX = x + mCellWidth / 2; final int centerY = y + mCellHeight / 2; int count = 0; - for (int i = 0; i < keys.size(); i++) { - final Key key = keys.get(i); + for (final Key key : keys) { if (key.isSpacer()) continue; - if (key.squaredDistanceToEdge(centerX, centerY) < threshold) - indices[count++] = i; + if (key.squaredDistanceToEdge(centerX, centerY) < threshold) { + neighborKeys[count++] = key; + } } - final int[] cell = new int[count]; - System.arraycopy(indices, 0, cell, 0, count); - mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = cell; + mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = + Arrays.copyOfRange(neighborKeys, 0, count); } } setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys, touchPositionCorrection); } - public int[] getNearestKeys(int x, int y) { + public Key[] getNearestKeys(int x, int y) { if (mGridNeighbors == null) { - return EMPTY_INT_ARRAY; + return EMPTY_KEY_ARRAY; } if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) { int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth); @@ -211,6 +204,6 @@ public class ProximityInfo { return mGridNeighbors[index]; } } - return EMPTY_INT_ARRAY; + return EMPTY_KEY_ARRAY; } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 3324fa6af..5dd8340fc 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -19,7 +19,9 @@ package com.android.inputmethod.keyboard.internal; import android.content.res.TypedArray; import android.util.Log; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -159,8 +161,8 @@ public class KeyStyles { readTextArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags); readInt(keyAttr, R.styleable.Keyboard_Key_keyIcon); + readInt(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled); readInt(keyAttr, R.styleable.Keyboard_Key_keyIconPreview); - readInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted); readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn); readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType); readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); @@ -197,7 +199,7 @@ public class KeyStyles { XmlPullParser parser) throws XmlPullParserException { final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName); if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />", - KeyboardBuilder.TAG_KEY_STYLE, styleName)); + Keyboard.Builder.TAG_KEY_STYLE, styleName)); if (mStyles.containsKey(styleName)) throw new XmlParseUtils.ParseException( "duplicate key style declared: " + styleName, parser); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java deleted file mode 100644 index 7382cfa7f..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ /dev/null @@ -1,833 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.keyboard.internal; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.TypedValue; -import android.util.Xml; -import android.view.InflateException; - -import com.android.inputmethod.compat.EditorInfoCompatUtils; -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.R; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.util.Arrays; - -/** - * Keyboard Building helper. - * - * This class parses Keyboard XML file and eventually build a Keyboard. - * The Keyboard XML file looks like: - * <pre> - * >!-- xml/keyboard.xml --< - * >Keyboard keyboard_attributes*< - * >!-- Keyboard Content --< - * >Row row_attributes*< - * >!-- Row Content --< - * >Key key_attributes* /< - * >Spacer horizontalGap="0.2in" /< - * >include keyboardLayout="@xml/other_keys"< - * ... - * >/Row< - * >include keyboardLayout="@xml/other_rows"< - * ... - * >/Keyboard< - * </pre> - * The XML file which is included in other file must have >merge< as root element, such as: - * <pre> - * >!-- xml/other_keys.xml --< - * >merge< - * >Key key_attributes* /< - * ... - * >/merge< - * </pre> - * and - * <pre> - * >!-- xml/other_rows.xml --< - * >merge< - * >Row row_attributes*< - * >Key key_attributes* /< - * >/Row< - * ... - * >/merge< - * </pre> - * You can also use switch-case-default tags to select Rows and Keys. - * <pre> - * >switch< - * >case case_attribute*< - * >!-- Any valid tags at switch position --< - * >/case< - * ... - * >default< - * >!-- Any valid tags at switch position --< - * >/default< - * >/switch< - * </pre> - * You can declare Key style and specify styles within Key tags. - * <pre> - * >switch< - * >case mode="email"< - * >key-style styleName="f1-key" parentStyle="modifier-key" - * keyLabel=".com" - * /< - * >/case< - * >case mode="url"< - * >key-style styleName="f1-key" parentStyle="modifier-key" - * keyLabel="http://" - * /< - * >/case< - * >/switch< - * ... - * >Key keyStyle="shift-key" ... /< - * </pre> - */ - -public class KeyboardBuilder<KP extends KeyboardParams> { - private static final String TAG = KeyboardBuilder.class.getSimpleName(); - private static final boolean DEBUG = false; - - // Keyboard XML Tags - private static final String TAG_KEYBOARD = "Keyboard"; - private static final String TAG_ROW = "Row"; - private static final String TAG_KEY = "Key"; - private static final String TAG_SPACER = "Spacer"; - private static final String TAG_INCLUDE = "include"; - private static final String TAG_MERGE = "merge"; - private static final String TAG_SWITCH = "switch"; - private static final String TAG_CASE = "case"; - private static final String TAG_DEFAULT = "default"; - public static final String TAG_KEY_STYLE = "key-style"; - - private static final int DEFAULT_KEYBOARD_COLUMNS = 10; - private static final int DEFAULT_KEYBOARD_ROWS = 4; - - protected final KP mParams; - protected final Context mContext; - protected final Resources mResources; - private final DisplayMetrics mDisplayMetrics; - - private int mCurrentY = 0; - private Row mCurrentRow = null; - private boolean mLeftEdge; - private boolean mTopEdge; - private Key mRightEdgeKey = null; - private final KeyStyles mKeyStyles = new KeyStyles(); - - /** - * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate. - * Some of the key size defaults can be overridden per row from what the {@link Keyboard} - * defines. - */ - public static class Row { - // keyWidth enum constants - private static final int KEYWIDTH_NOT_ENUM = 0; - private static final int KEYWIDTH_FILL_RIGHT = -1; - private static final int KEYWIDTH_FILL_BOTH = -2; - - private final KeyboardParams mParams; - /** Default width of a key in this row. */ - public final float mDefaultKeyWidth; - /** Default height of a key in this row. */ - public final int mRowHeight; - - private final int mCurrentY; - // Will be updated by {@link Key}'s constructor. - private float mCurrentX; - - public Row(Resources res, KeyboardParams params, XmlPullParser parser, int y) { - mParams = params; - TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard); - mRowHeight = (int)KeyboardBuilder.getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight); - keyboardAttr.recycle(); - TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_Key); - mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth, params.mDefaultKeyWidth); - keyAttr.recycle(); - - mCurrentY = y; - mCurrentX = 0.0f; - } - - public void setXPos(float keyXPos) { - mCurrentX = keyXPos; - } - - public void advanceXPos(float width) { - mCurrentX += width; - } - - public int getKeyY() { - return mCurrentY; - } - - public float getKeyX(TypedArray keyAttr) { - final int widthType = KeyboardBuilder.getEnumValue(keyAttr, - R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM); - if (widthType == KEYWIDTH_FILL_BOTH) { - // If keyWidth is fillBoth, the key width should start right after the nearest key - // on the left hand side. - return mCurrentX; - } - - final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding; - if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) { - final float keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0); - if (keyXPos < 0) { - // If keyXPos is negative, the actual x-coordinate will be - // keyboardWidth + keyXPos. - // keyXPos shouldn't be less than mCurrentX because drawable area for this key - // starts at mCurrentX. Or, this key will overlaps the adjacent key on its left - // hand side. - return Math.max(keyXPos + keyboardRightEdge, mCurrentX); - } else { - return keyXPos + mParams.mHorizontalEdgesPadding; - } - } - return mCurrentX; - } - - public float getKeyWidth(TypedArray keyAttr, float keyXPos) { - final int widthType = KeyboardBuilder.getEnumValue(keyAttr, - R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM); - switch (widthType) { - case KEYWIDTH_FILL_RIGHT: - case KEYWIDTH_FILL_BOTH: - final int keyboardRightEdge = - mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding; - // If keyWidth is fillRight, the actual key width will be determined to fill out the - // area up to the right edge of the keyboard. - // If keyWidth is fillBoth, the actual key width will be determined to fill out the - // area between the nearest key on the left hand side and the right edge of the - // keyboard. - return keyboardRightEdge - keyXPos; - default: // KEYWIDTH_NOT_ENUM - return KeyboardBuilder.getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_keyWidth, mParams.mBaseWidth, mDefaultKeyWidth); - } - } - } - - public KeyboardBuilder(Context context, KP params) { - mContext = context; - final Resources res = context.getResources(); - mResources = res; - mDisplayMetrics = res.getDisplayMetrics(); - - mParams = params; - - setTouchPositionCorrectionData(context, params); - - params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); - params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); - } - - private static void setTouchPositionCorrectionData(Context context, KeyboardParams params) { - final TypedArray a = context.obtainStyledAttributes( - null, R.styleable.Keyboard, R.attr.keyboardStyle, 0); - params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0); - final int resourceId = a.getResourceId(R.styleable.Keyboard_touchPositionCorrectionData, 0); - a.recycle(); - if (resourceId == 0) { - if (LatinImeLogger.sDBG) - throw new RuntimeException("touchPositionCorrectionData is not defined"); - return; - } - - final String[] data = context.getResources().getStringArray(resourceId); - params.mTouchPositionCorrection.load(data); - } - - public KeyboardBuilder<KP> load(KeyboardId id) { - mParams.mId = id; - final XmlResourceParser parser = mResources.getXml(id.getXmlId()); - try { - parseKeyboard(parser); - } catch (XmlPullParserException e) { - Log.w(TAG, "keyboard XML parse error: " + e); - throw new IllegalArgumentException(e); - } catch (IOException e) { - Log.w(TAG, "keyboard XML parse error: " + e); - throw new RuntimeException(e); - } finally { - parser.close(); - } - return this; - } - - public void setTouchPositionCorrectionEnabled(boolean enabled) { - mParams.mTouchPositionCorrection.setEnabled(enabled); - } - - public Keyboard build() { - return new Keyboard(mParams); - } - - private void parseKeyboard(XmlPullParser parser) - throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId)); - int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - if (TAG_KEYBOARD.equals(tag)) { - parseKeyboardAttributes(parser); - startKeyboard(); - parseKeyboardContent(parser, false); - break; - } else { - throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD); - } - } - } - } - - private void parseKeyboardAttributes(XmlPullParser parser) { - final int displayWidth = mDisplayMetrics.widthPixels; - final TypedArray keyboardAttr = mContext.obtainStyledAttributes( - Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle, - R.style.Keyboard); - final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_Key); - try { - final int displayHeight = mDisplayMetrics.heightPixels; - final int keyboardHeight = (int)keyboardAttr.getDimension( - R.styleable.Keyboard_keyboardHeight, displayHeight / 2); - final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2); - int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2); - if (minKeyboardHeight < 0) { - // Specified fraction was negative, so it should be calculated against display - // width. - minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2); - } - final KeyboardParams params = mParams; - // Keyboard height will not exceed maxKeyboardHeight and will not be less than - // minKeyboardHeight. - params.mOccupiedHeight = Math.max( - Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); - params.mOccupiedWidth = params.mId.mWidth; - params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0); - params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0); - params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyboardHorizontalEdgesPadding, mParams.mOccupiedWidth, 0); - - params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2 - - params.mHorizontalCenterPadding; - params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth, - params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS); - params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0); - params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0); - params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding - - params.mBottomPadding + params.mVerticalGap; - params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_rowHeight, params.mBaseHeight, - params.mBaseHeight / DEFAULT_KEYBOARD_ROWS); - - params.mIsRtlKeyboard = keyboardAttr.getBoolean( - R.styleable.Keyboard_isRtlKeyboard, false); - params.mMoreKeysTemplate = keyboardAttr.getResourceId( - R.styleable.Keyboard_moreKeysTemplate, 0); - params.mMaxMiniKeyboardColumn = keyAttr.getInt( - R.styleable.Keyboard_Key_maxMoreKeysColumn, 5); - - params.mIconsSet.loadIcons(keyboardAttr); - } finally { - keyAttr.recycle(); - keyboardAttr.recycle(); - } - } - - private void parseKeyboardContent(XmlPullParser parser, boolean skip) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - if (TAG_ROW.equals(tag)) { - Row row = parseRowAttributes(parser); - if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW)); - if (!skip) - startRow(row); - parseRowContent(parser, row, skip); - } else if (TAG_INCLUDE.equals(tag)) { - parseIncludeKeyboardContent(parser, skip); - } else if (TAG_SWITCH.equals(tag)) { - parseSwitchKeyboardContent(parser, skip); - } else if (TAG_KEY_STYLE.equals(tag)) { - parseKeyStyle(parser, skip); - } else { - throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW); - } - } else if (event == XmlPullParser.END_TAG) { - final String tag = parser.getName(); - if (TAG_KEYBOARD.equals(tag)) { - endKeyboard(); - break; - } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) - || TAG_MERGE.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", tag)); - break; - } else if (TAG_KEY_STYLE.equals(tag)) { - continue; - } else { - throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW); - } - } - } - } - - private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException { - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard); - try { - if (a.hasValue(R.styleable.Keyboard_horizontalGap)) - throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap"); - if (a.hasValue(R.styleable.Keyboard_verticalGap)) - throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap"); - return new Row(mResources, mParams, parser, mCurrentY); - } finally { - a.recycle(); - } - } - - private void parseRowContent(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - if (TAG_KEY.equals(tag)) { - parseKey(parser, row, skip); - } else if (TAG_SPACER.equals(tag)) { - parseSpacer(parser, row, skip); - } else if (TAG_INCLUDE.equals(tag)) { - parseIncludeRowContent(parser, row, skip); - } else if (TAG_SWITCH.equals(tag)) { - parseSwitchRowContent(parser, row, skip); - } else if (TAG_KEY_STYLE.equals(tag)) { - parseKeyStyle(parser, skip); - } else { - throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY); - } - } else if (event == XmlPullParser.END_TAG) { - final String tag = parser.getName(); - if (TAG_ROW.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW)); - if (!skip) - endRow(row); - break; - } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) - || TAG_MERGE.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", tag)); - break; - } else if (TAG_KEY_STYLE.equals(tag)) { - continue; - } else { - throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); - } - } - } - } - - private void parseKey(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - if (skip) { - XmlParseUtils.checkEndTag(TAG_KEY, parser); - } else { - final Key key = new Key(mResources, mParams, row, parser, mKeyStyles); - if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />", - TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode, - Arrays.toString(key.mMoreKeys))); - XmlParseUtils.checkEndTag(TAG_KEY, parser); - endKey(key); - } - } - - private void parseSpacer(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - if (skip) { - XmlParseUtils.checkEndTag(TAG_SPACER, parser); - } else { - final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser, mKeyStyles); - if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); - XmlParseUtils.checkEndTag(TAG_SPACER, parser); - endKey(spacer); - } - } - - private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip) - throws XmlPullParserException, IOException { - parseIncludeInternal(parser, null, skip); - } - - private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - parseIncludeInternal(parser, row, skip); - } - - private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - if (skip) { - XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); - } else { - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_Include); - int keyboardLayout = 0; - try { - XmlParseUtils.checkAttributeExists(a, - R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout", - TAG_INCLUDE, parser); - keyboardLayout = a.getResourceId(R.styleable.Keyboard_Include_keyboardLayout, 0); - } finally { - a.recycle(); - } - - XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); - if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", - TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); - final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout); - try { - parseMerge(parserForInclude, row, skip); - } finally { - parserForInclude.close(); - } - } - } - - private void parseMerge(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - if (TAG_MERGE.equals(tag)) { - if (row == null) { - parseKeyboardContent(parser, skip); - } else { - parseRowContent(parser, row, skip); - } - break; - } else { - throw new XmlParseUtils.ParseException( - "Included keyboard layout must have <merge> root element", parser); - } - } - } - } - - private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip) - throws XmlPullParserException, IOException { - parseSwitchInternal(parser, null, skip); - } - - private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - parseSwitchInternal(parser, row, skip); - } - - private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId)); - boolean selected = false; - int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - if (TAG_CASE.equals(tag)) { - selected |= parseCase(parser, row, selected ? true : skip); - } else if (TAG_DEFAULT.equals(tag)) { - selected |= parseDefault(parser, row, selected ? true : skip); - } else { - throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY); - } - } else if (event == XmlPullParser.END_TAG) { - final String tag = parser.getName(); - if (TAG_SWITCH.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH)); - break; - } else { - throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); - } - } - } - } - - private boolean parseCase(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - final boolean selected = parseCaseCondition(parser); - if (row == null) { - // Processing Rows. - parseKeyboardContent(parser, selected ? skip : true); - } else { - // Processing Keys. - parseRowContent(parser, row, selected ? skip : true); - } - return selected; - } - - private boolean parseCaseCondition(XmlPullParser parser) { - final KeyboardId id = mParams.mId; - if (id == null) - return true; - - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_Case); - try { - final boolean modeMatched = matchTypedValue(a, - R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode)); - final boolean navigateActionMatched = matchBoolean(a, - R.styleable.Keyboard_Case_navigateAction, id.navigateAction()); - final boolean passwordInputMatched = matchBoolean(a, - R.styleable.Keyboard_Case_passwordInput, id.passwordInput()); - final boolean hasSettingsKeyMatched = matchBoolean(a, - R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey()); - final boolean f2KeyModeMatched = matchInteger(a, - R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode()); - final boolean clobberSettingsKeyMatched = matchBoolean(a, - R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); - final boolean shortcutKeyEnabledMatched = matchBoolean(a, - R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled); - final boolean hasShortcutKeyMatched = matchBoolean(a, - R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); - // As noted at {@link KeyboardId} class, we are interested only in enum value masked by - // {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and - // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching - // this attribute with id.mImeOptions as integer value is enough for our purpose. - final boolean imeActionMatched = matchInteger(a, - R.styleable.Keyboard_Case_imeAction, id.imeAction()); - final boolean localeCodeMatched = matchString(a, - R.styleable.Keyboard_Case_localeCode, id.mLocale.toString()); - final boolean languageCodeMatched = matchString(a, - R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage()); - final boolean countryCodeMatched = matchString(a, - R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); - final boolean selected = modeMatched && navigateActionMatched && passwordInputMatched - && hasSettingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched - && shortcutKeyEnabledMatched && hasShortcutKeyMatched && imeActionMatched && - localeCodeMatched && languageCodeMatched && countryCodeMatched; - - if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, - textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"), - booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"), - booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), - booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"), - textAttr(KeyboardId.f2KeyModeName( - a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"), - booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey, - "clobberSettingsKey"), - booleanAttr( - a, R.styleable.Keyboard_Case_shortcutKeyEnabled, "shortcutKeyEnabled"), - booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), - textAttr(EditorInfoCompatUtils.imeOptionsName( - a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"), - textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"), - textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), "languageCode"), - textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"), - Boolean.toString(selected))); - - return selected; - } finally { - a.recycle(); - } - } - - private static boolean matchInteger(TypedArray a, int index, int value) { - // If <case> does not have "index" attribute, that means this <case> is wild-card for the - // attribute. - return !a.hasValue(index) || a.getInt(index, 0) == value; - } - - private static boolean matchBoolean(TypedArray a, int index, boolean value) { - // If <case> does not have "index" attribute, that means this <case> is wild-card for the - // attribute. - return !a.hasValue(index) || a.getBoolean(index, false) == value; - } - - private static boolean matchString(TypedArray a, int index, String value) { - // If <case> does not have "index" attribute, that means this <case> is wild-card for the - // attribute. - return !a.hasValue(index) || stringArrayContains(a.getString(index).split("\\|"), value); - } - - private static boolean matchTypedValue(TypedArray a, int index, int intValue, String strValue) { - // If <case> does not have "index" attribute, that means this <case> is wild-card for the - // attribute. - final TypedValue v = a.peekValue(index); - if (v == null) - return true; - - if (isIntegerValue(v)) { - return intValue == a.getInt(index, 0); - } else if (isStringValue(v)) { - return stringArrayContains(a.getString(index).split("\\|"), strValue); - } - return false; - } - - private static boolean stringArrayContains(String[] array, String value) { - for (final String elem : array) { - if (elem.equals(value)) - return true; - } - return false; - } - - private boolean parseDefault(XmlPullParser parser, Row row, boolean skip) - throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT)); - if (row == null) { - parseKeyboardContent(parser, skip); - } else { - parseRowContent(parser, row, skip); - } - return true; - } - - private void parseKeyStyle(XmlPullParser parser, boolean skip) - throws XmlPullParserException { - TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_KeyStyle); - TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_Key); - try { - if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) - throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE - + "/> needs styleName attribute", parser); - if (!skip) - mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); - } finally { - keyStyleAttr.recycle(); - keyAttrs.recycle(); - } - } - - private void startKeyboard() { - mCurrentY += mParams.mTopPadding; - mTopEdge = true; - } - - private void startRow(Row row) { - addEdgeSpace(mParams.mHorizontalEdgesPadding, row); - mCurrentRow = row; - mLeftEdge = true; - mRightEdgeKey = null; - } - - private void endRow(Row row) { - if (mCurrentRow == null) - throw new InflateException("orphant end row tag"); - if (mRightEdgeKey != null) { - mRightEdgeKey.markAsRightEdge(mParams); - mRightEdgeKey = null; - } - addEdgeSpace(mParams.mHorizontalEdgesPadding, row); - mCurrentY += row.mRowHeight; - mCurrentRow = null; - mTopEdge = false; - } - - private void endKey(Key key) { - mParams.onAddKey(key); - if (mLeftEdge) { - key.markAsLeftEdge(mParams); - mLeftEdge = false; - } - if (mTopEdge) { - key.markAsTopEdge(mParams); - } - mRightEdgeKey = key; - } - - private void endKeyboard() { - // nothing to do here. - } - - private void addEdgeSpace(float width, Row row) { - row.advanceXPos(width); - mLeftEdge = false; - mRightEdgeKey = null; - } - - public static float getDimensionOrFraction(TypedArray a, int index, int base, float defValue) { - final TypedValue value = a.peekValue(index); - if (value == null) - return defValue; - if (isFractionValue(value)) { - return a.getFraction(index, base, base, defValue); - } else if (isDimensionValue(value)) { - return a.getDimension(index, defValue); - } - return defValue; - } - - public static int getEnumValue(TypedArray a, int index, int defValue) { - final TypedValue value = a.peekValue(index); - if (value == null) - return defValue; - if (isIntegerValue(value)) { - return a.getInt(index, defValue); - } - return defValue; - } - - private static boolean isFractionValue(TypedValue v) { - return v.type == TypedValue.TYPE_FRACTION; - } - - private static boolean isDimensionValue(TypedValue v) { - return v.type == TypedValue.TYPE_DIMENSION; - } - - private static boolean isIntegerValue(TypedValue v) { - return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT; - } - - private static boolean isStringValue(TypedValue v) { - return v.type == TypedValue.TYPE_STRING; - } - - private static String textAttr(String value, String name) { - return value != null ? String.format(" %s=%s", name, value) : ""; - } - - private static String booleanAttr(TypedArray a, int index, String name) { - return a.hasValue(index) ? String.format(" %s=%s", name, a.getBoolean(index, false)) : ""; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index faa5f86f2..6313a61b5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -23,80 +23,75 @@ import android.util.Log; import com.android.inputmethod.latin.R; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + public class KeyboardIconsSet { private static final String TAG = KeyboardIconsSet.class.getSimpleName(); public static final int ICON_UNDEFINED = 0; - // This should be aligned with Keyboard.keyIcon enum. - private static final int ICON_SHIFT_KEY = 1; - private static final int ICON_DELETE_KEY = 2; - private static final int ICON_SETTINGS_KEY = 3; // This is also represented as "@icon/3" in XML. - private static final int ICON_SPACE_KEY = 4; - private static final int ICON_RETURN_KEY = 5; - private static final int ICON_SEARCH_KEY = 6; - private static final int ICON_TAB_KEY = 7; // This is also represented as "@icon/7" in XML. - private static final int ICON_SHORTCUT_KEY = 8; - private static final int ICON_SHORTCUT_FOR_LABEL = 9; - // This should be aligned with Keyboard.keyIconShifted enum. - private static final int ICON_SHIFTED_SHIFT_KEY = 10; - // This should be aligned with Keyboard.keyIconPreview enum. - private static final int ICON_PREVIEW_TAB_KEY = 11; + private final Map<Integer, Drawable> mIcons = new HashMap<Integer, Drawable>(); - private static final int ICON_LAST = 11; + // The key value should be aligned with the enum value of Keyboard.icon*. + private static final Map<Integer, Integer> ICONS_TO_ATTRS_MAP = new HashMap<Integer, Integer>(); + private static final Collection<Integer> VALID_ATTRS; - private final Drawable mIcons[] = new Drawable[ICON_LAST + 1]; + static { + addIconIdMap(1, R.styleable.Keyboard_iconShiftKey); + addIconIdMap(2, R.styleable.Keyboard_iconDeleteKey); + // This is also represented as "@icon/3" in keyboard layout XML. + addIconIdMap(3, R.styleable.Keyboard_iconSettingsKey); + addIconIdMap(4, R.styleable.Keyboard_iconSpaceKey); + addIconIdMap(5, R.styleable.Keyboard_iconReturnKey); + addIconIdMap(6, R.styleable.Keyboard_iconSearchKey); + // This is also represented as "@icon/7" in keyboard layout XML. + addIconIdMap(7, R.styleable.Keyboard_iconTabKey); + addIconIdMap(8, R.styleable.Keyboard_iconShortcutKey); + addIconIdMap(9, R.styleable.Keyboard_iconShortcutForLabel); + addIconIdMap(10, R.styleable.Keyboard_iconSpaceKeyForNumberLayout); + addIconIdMap(11, R.styleable.Keyboard_iconShiftKeyShifted); + addIconIdMap(12, R.styleable.Keyboard_iconDisabledShortcutKey); + addIconIdMap(13, R.styleable.Keyboard_iconPreviewTabKey); + VALID_ATTRS = ICONS_TO_ATTRS_MAP.values(); + } - private static final int getIconId(final int attrIndex) { - switch (attrIndex) { - case R.styleable.Keyboard_iconShiftKey: - return ICON_SHIFT_KEY; - case R.styleable.Keyboard_iconDeleteKey: - return ICON_DELETE_KEY; - case R.styleable.Keyboard_iconSettingsKey: - return ICON_SETTINGS_KEY; - case R.styleable.Keyboard_iconSpaceKey: - return ICON_SPACE_KEY; - case R.styleable.Keyboard_iconReturnKey: - return ICON_RETURN_KEY; - case R.styleable.Keyboard_iconSearchKey: - return ICON_SEARCH_KEY; - case R.styleable.Keyboard_iconTabKey: - return ICON_TAB_KEY; - case R.styleable.Keyboard_iconShortcutKey: - return ICON_SHORTCUT_KEY; - case R.styleable.Keyboard_iconShortcutForLabel: - return ICON_SHORTCUT_FOR_LABEL; - case R.styleable.Keyboard_iconShiftedShiftKey: - return ICON_SHIFTED_SHIFT_KEY; - case R.styleable.Keyboard_iconPreviewTabKey: - return ICON_PREVIEW_TAB_KEY; - default: - return ICON_UNDEFINED; - } + private static void addIconIdMap(int iconId, int attrId) { + ICONS_TO_ATTRS_MAP.put(iconId, attrId); } public void loadIcons(final TypedArray keyboardAttrs) { - final int count = keyboardAttrs.getIndexCount(); - for (int i = 0; i < count; i++) { - final int attrIndex = keyboardAttrs.getIndex(i); - final int iconId = getIconId(attrIndex); - if (iconId != ICON_UNDEFINED) { - try { - mIcons[iconId] = setDefaultBounds(keyboardAttrs.getDrawable(attrIndex)); - } catch (Resources.NotFoundException e) { - Log.w(TAG, "Drawable resource for icon #" + iconId + " not found"); - } + for (final Integer attrId : VALID_ATTRS) { + try { + final Drawable icon = keyboardAttrs.getDrawable(attrId); + if (icon == null) continue; + setDefaultBounds(icon); + mIcons.put(attrId, icon); + } catch (Resources.NotFoundException e) { + Log.w(TAG, "Drawable resource for icon #" + + keyboardAttrs.getResources().getResourceEntryName(attrId) + + " not found"); } } } - public Drawable getIcon(final int iconId) { - if (iconId == ICON_UNDEFINED) + public Drawable getIconByIconId(final Integer iconId) { + if (iconId == ICON_UNDEFINED) { return null; - if (iconId < 0 || iconId >= mIcons.length) + } + final Integer attrId = ICONS_TO_ATTRS_MAP.get(iconId); + if (attrId == null) { throw new IllegalArgumentException("icon id is out of range: " + iconId); - return mIcons[iconId]; + } + return getIconByAttrId(attrId); + } + + public Drawable getIconByAttrId(final Integer attrId) { + if (!VALID_ATTRS.contains(attrId)) { + throw new IllegalArgumentException("unknown icon attribute id: " + attrId); + } + return mIcons.get(attrId); } private static Drawable setDefaultBounds(final Drawable icon) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java deleted file mode 100644 index 64cd37c4b..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.keyboard.internal; - -import android.graphics.drawable.Drawable; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.latin.LatinImeLogger; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class KeyboardParams { - public KeyboardId mId; - public int mThemeId; - - /** Total height and width of the keyboard, including the paddings and keys */ - public int mOccupiedHeight; - public int mOccupiedWidth; - - /** Base height and width of the keyboard used to calculate rows' or keys' heights and widths */ - public int mBaseHeight; - public int mBaseWidth; - - public int mTopPadding; - public int mBottomPadding; - public int mHorizontalEdgesPadding; - public int mHorizontalCenterPadding; - - public int mDefaultRowHeight; - public int mDefaultKeyWidth; - public int mHorizontalGap; - public int mVerticalGap; - - public boolean mIsRtlKeyboard; - public int mMoreKeysTemplate; - public int mMaxMiniKeyboardColumn; - - public int GRID_WIDTH; - public int GRID_HEIGHT; - - public final List<Key> mKeys = new ArrayList<Key>(); - public final List<Key> mShiftKeys = new ArrayList<Key>(); - public final Set<Key> mShiftLockKeys = new HashSet<Key>(); - public final Map<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>(); - public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>(); - public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); - - public int mMostCommonKeyHeight = 0; - public int mMostCommonKeyWidth = 0; - - public final TouchPositionCorrection mTouchPositionCorrection = new TouchPositionCorrection(); - - public static class TouchPositionCorrection { - private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3; - - public boolean mEnabled; - public float[] mXs; - public float[] mYs; - public float[] mRadii; - - public void load(String[] data) { - final int dataLength = data.length; - if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) { - if (LatinImeLogger.sDBG) - throw new RuntimeException( - "the size of touch position correction data is invalid"); - return; - } - - final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE; - mXs = new float[length]; - mYs = new float[length]; - mRadii = new float[length]; - try { - for (int i = 0; i < dataLength; ++i) { - final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE; - final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE; - final float value = Float.parseFloat(data[i]); - if (type == 0) { - mXs[index] = value; - } else if (type == 1) { - mYs[index] = value; - } else { - mRadii[index] = value; - } - } - } catch (NumberFormatException e) { - if (LatinImeLogger.sDBG) { - throw new RuntimeException( - "the number format for touch position correction data is invalid"); - } - mXs = null; - mYs = null; - mRadii = null; - } - } - - public void setEnabled(boolean enabled) { - mEnabled = enabled; - } - - public boolean isValid() { - return mEnabled && mXs != null && mYs != null && mRadii != null - && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0; - } - } - - protected void clearKeys() { - mKeys.clear(); - mShiftKeys.clear(); - mShiftLockKeys.clear(); - mShiftedIcons.clear(); - mUnshiftedIcons.clear(); - clearHistogram(); - } - - public void onAddKey(Key key) { - mKeys.add(key); - updateHistogram(key); - if (key.mCode == Keyboard.CODE_SHIFT) { - mShiftKeys.add(key); - if (key.isSticky()) { - mShiftLockKeys.add(key); - } - } - } - - public void addShiftedIcon(Key key, Drawable icon) { - mUnshiftedIcons.put(key, key.getIcon()); - mShiftedIcons.put(key, icon); - } - - private int mMaxHeightCount = 0; - private int mMaxWidthCount = 0; - private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>(); - private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>(); - - private void clearHistogram() { - mMostCommonKeyHeight = 0; - mMaxHeightCount = 0; - mHeightHistogram.clear(); - - mMaxWidthCount = 0; - mMostCommonKeyWidth = 0; - mWidthHistogram.clear(); - } - - private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) { - final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1; - histogram.put(key, count); - return count; - } - - private void updateHistogram(Key key) { - final Integer height = key.mHeight + key.mVerticalGap; - final int heightCount = updateHistogramCounter(mHeightHistogram, height); - if (heightCount > mMaxHeightCount) { - mMaxHeightCount = heightCount; - mMostCommonKeyHeight = height; - } - - final Integer width = key.mWidth + key.mHorizontalGap; - final int widthCount = updateHistogramCounter(mWidthHistogram, width); - if (widthCount > mMaxWidthCount) { - mMaxWidthCount = widthCount; - mMostCommonKeyWidth = width; - } - } -} diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java new file mode 100644 index 000000000..78b2de342 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.text.InputType; +import android.util.Log; +import android.view.inputmethod.EditorInfo; + +import com.android.inputmethod.compat.InputTypeCompatUtils; + +/** + * Class to hold attributes of the input field. + */ +public class InputAttributes { + private final String TAG = InputAttributes.class.getSimpleName(); + + final public boolean mInsertSpaceOnPickSuggestionManually; + final public boolean mInputTypeNoAutoCorrect; + final public boolean mIsSettingsSuggestionStripOn; + final public boolean mApplicationSpecifiedCompletionOn; + + public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) { + if (editorInfo == null || editorInfo.inputType == InputType.TYPE_CLASS_TEXT) { + mInsertSpaceOnPickSuggestionManually = false; + mIsSettingsSuggestionStripOn = false; + mInputTypeNoAutoCorrect = false; + mApplicationSpecifiedCompletionOn = false; + } else { + final int inputType = editorInfo.inputType; + if (inputType == InputType.TYPE_NULL) { + // TODO: We should honor TYPE_NULL specification. + Log.i(TAG, "InputType.TYPE_NULL is specified"); + } + final int inputClass = inputType & InputType.TYPE_MASK_CLASS; + final int variation = inputType & InputType.TYPE_MASK_VARIATION; + if (inputClass == 0) { + // TODO: is this check still necessary? + Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" + + " imeOptions=0x%08x", + inputType, editorInfo.imeOptions)); + } + final boolean flagNoSuggestions = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + final boolean flagMultiLine = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE); + final boolean flagAutoCorrect = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); + final boolean flagAutoComplete = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); + + // Make sure that passwords are not displayed in {@link SuggestionsView}. + if (InputTypeCompatUtils.isPasswordInputType(inputType) + || InputTypeCompatUtils.isVisiblePasswordInputType(inputType) + || InputTypeCompatUtils.isEmailVariation(variation) + || InputType.TYPE_TEXT_VARIATION_URI == variation + || InputType.TYPE_TEXT_VARIATION_FILTER == variation + || flagNoSuggestions + || flagAutoComplete) { + mIsSettingsSuggestionStripOn = false; + } else { + mIsSettingsSuggestionStripOn = true; + } + + if (InputTypeCompatUtils.isEmailVariation(variation) + || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { + // The point in turning this off is that we don't want to insert a space after + // a name when filling a form: we can't delete trailing spaces when changing fields + mInsertSpaceOnPickSuggestionManually = false; + } else { + mInsertSpaceOnPickSuggestionManually = true; + } + + // If it's a browser edit field and auto correct is not ON explicitly, then + // disable auto correction, but keep suggestions on. + // If NO_SUGGESTIONS is set, don't do prediction. + // If it's not multiline and the autoCorrect flag is not set, then don't correct + if ((variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT + && !flagAutoCorrect) + || flagNoSuggestions + || (!flagAutoCorrect && !flagMultiLine)) { + mInputTypeNoAutoCorrect = true; + } else { + mInputTypeNoAutoCorrect = false; + } + + mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode; + } + } + + // Pretty print + @Override + public String toString() { + return "\n mInsertSpaceOnPickSuggestionManually = " + mInsertSpaceOnPickSuggestionManually + + "\n mInputTypeNoAutoCorrect = " + mInputTypeNoAutoCorrect + + "\n mIsSettingsSuggestionStripOn = " + mIsSettingsSuggestionStripOn + + "\n mApplicationSpecifiedCompletionOn = " + mApplicationSpecifiedCompletionOn; + } +} diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index a0c59671f..60f8ce8d8 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -65,8 +65,8 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.keyboard.LatinKeyboardView; +import com.android.inputmethod.latin.suggestions.SuggestionsView; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -175,6 +175,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private int mSpaceState; private SettingsValues mSettingsValues; + private InputAttributes mInputAttributes; private View mExtractArea; private View mKeyPreviewBackingView; @@ -195,18 +196,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private UserUnigramDictionary mUserUnigramDictionary; private boolean mIsUserDictionaryAvailable; - // TODO: Create an inner class to group options and pseudo-options to improve readability. - // These variables are initialized according to the {@link EditorInfo#inputType}. - private boolean mInsertSpaceOnPickSuggestionManually; - private boolean mInputTypeNoAutoCorrect; - private boolean mIsSettingsSuggestionStripOn; - private boolean mApplicationSpecifiedCompletionOn; - private WordComposer mWordComposer = new WordComposer(); - private boolean mHasUncommittedTypedChars; private int mCorrectionMode; - private String mWordSavedForAutoCorrectCancellation; // Keep track of the last selection range to decide if we need to show word alternatives private int mLastSelectionStart; private int mLastSelectionEnd; @@ -299,13 +291,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR: setSpacebarTextFadeFactor(inputView, (1.0f + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, - (LatinKeyboard)msg.obj); + (Keyboard)msg.obj); sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj), mDurationOfFadeoutLanguageOnSpacebar); break; case MSG_DISMISS_LANGUAGE_ON_SPACEBAR: setSpacebarTextFadeFactor(inputView, mFinalFadeoutFactorOfLanguageOnSpacebar, - (LatinKeyboard)msg.obj); + (Keyboard)msg.obj); break; } } @@ -346,15 +338,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } private static void setSpacebarTextFadeFactor(LatinKeyboardView inputView, - float fadeFactor, LatinKeyboard oldKeyboard) { + float fadeFactor, Keyboard oldKeyboard) { if (inputView == null) return; final Keyboard keyboard = inputView.getKeyboard(); - if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) { - final Key updatedKey = ((LatinKeyboard)keyboard).updateSpacebarLanguage( - fadeFactor, - Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */), - SubtypeSwitcher.getInstance().needsToDisplayLanguage(keyboard.mId.mLocale)); - inputView.invalidateKey(updatedKey); + if (keyboard == oldKeyboard) { + inputView.updateSpacebar(fadeFactor, + SubtypeSwitcher.getInstance().needsToDisplayLanguage( + keyboard.mId.mLocale)); } } @@ -364,7 +354,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR); final LatinKeyboardView inputView = latinIme.mKeyboardSwitcher.getKeyboardView(); if (inputView != null) { - final LatinKeyboard keyboard = latinIme.mKeyboardSwitcher.getLatinKeyboard(); + final Keyboard keyboard = latinIme.mKeyboardSwitcher.getKeyboard(); // The language is always displayed when the delay is negative. final boolean needsToDisplayLanguage = localeChanged || mDelayBeforeFadeoutLanguageOnSpacebar < 0; @@ -498,7 +488,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar InputMethodManagerCompatWrapper.init(this); SubtypeSwitcher.init(this); KeyboardSwitcher.init(this, prefs); - AccessibilityUtils.init(this, prefs); + AccessibilityUtils.init(this); super.onCreate(); @@ -514,6 +504,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar loadSettings(); + // TODO: remove the following when it's not needed by updateCorrectionMode() any more + mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); Utils.GCUtils.getInstance().reset(); boolean tryGC = true; for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { @@ -756,18 +748,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // The EditorInfo might have a flag that affects fullscreen mode. // Note: This call should be done by InputMethodService? updateFullscreenMode(); - initializeInputAttributes(editorInfo); + mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); + mApplicationSpecifiedCompletions = null; inputView.closing(); mEnteredText = null; mWordComposer.reset(); - mHasUncommittedTypedChars = false; mDeleteCount = 0; mSpaceState = SPACE_STATE_NONE; loadSettings(); updateCorrectionMode(); - updateSuggestionVisibility(mPrefs, mResources); + updateSuggestionVisibility(mResources); if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) { mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); @@ -797,73 +789,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } - private void initializeInputAttributes(EditorInfo editorInfo) { - if (editorInfo == null) - return; - final int inputType = editorInfo.inputType; - if (inputType == InputType.TYPE_NULL) { - // TODO: We should honor TYPE_NULL specification. - Log.i(TAG, "InputType.TYPE_NULL is specified"); - } - final int inputClass = inputType & InputType.TYPE_MASK_CLASS; - final int variation = inputType & InputType.TYPE_MASK_VARIATION; - if (inputClass == 0) { - Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x imeOptions=0x%08x", - inputType, editorInfo.imeOptions)); - } - - mInsertSpaceOnPickSuggestionManually = false; - mInputTypeNoAutoCorrect = false; - mIsSettingsSuggestionStripOn = false; - mApplicationSpecifiedCompletionOn = false; - mApplicationSpecifiedCompletions = null; - - if (inputClass == InputType.TYPE_CLASS_TEXT) { - mIsSettingsSuggestionStripOn = true; - // Make sure that passwords are not displayed in {@link SuggestionsView}. - if (InputTypeCompatUtils.isPasswordInputType(inputType) - || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) { - mIsSettingsSuggestionStripOn = false; - } - if (InputTypeCompatUtils.isEmailVariation(variation) - || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { - // The point in turning this off is that we don't want to insert a space after - // a name when filling a form: we can't delete trailing spaces when changing fields - mInsertSpaceOnPickSuggestionManually = false; - } else { - mInsertSpaceOnPickSuggestionManually = true; - } - if (InputTypeCompatUtils.isEmailVariation(variation)) { - mIsSettingsSuggestionStripOn = false; - } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { - mIsSettingsSuggestionStripOn = false; - } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { - mIsSettingsSuggestionStripOn = false; - } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { - // If it's a browser edit field and auto correct is not ON explicitly, then - // disable auto correction, but keep suggestions on. - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) { - mInputTypeNoAutoCorrect = true; - } - } - - // If NO_SUGGESTIONS is set, don't do prediction. - if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) { - mIsSettingsSuggestionStripOn = false; - mInputTypeNoAutoCorrect = true; - } - // If it's not multiline and the autoCorrect flag is not set, then don't correct - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 - && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) { - mInputTypeNoAutoCorrect = true; - } - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { - mIsSettingsSuggestionStripOn = false; - mApplicationSpecifiedCompletionOn = isFullscreenMode(); - } - } - } - @Override public void onWindowHidden() { super.onWindowHidden(); @@ -932,11 +857,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // newly inserted punctuation. mSpaceState = SPACE_STATE_NONE; } - if (((mWordComposer.size() > 0 && mHasUncommittedTypedChars) + if (((mWordComposer.isComposingWord()) || mVoiceProxy.isVoiceInputHighlighted()) && (selectionChanged || candidatesCleared)) { mWordComposer.reset(); - mHasUncommittedTypedChars = false; updateSuggestions(); final InputConnection ic = getCurrentInputConnection(); if (ic != null) { @@ -944,7 +868,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } mComposingStateManager.onFinishComposingText(); mVoiceProxy.setVoiceInputHighlighted(false); - } else if (!mHasUncommittedTypedChars) { + } else if (!mWordComposer.isComposingWord()) { updateSuggestions(); } } @@ -1015,7 +939,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } } - if (mApplicationSpecifiedCompletionOn) { + if (mInputAttributes.mApplicationSpecifiedCompletionOn) { mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestions(); @@ -1145,9 +1069,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public void commitTyped(final InputConnection ic) { - if (!mHasUncommittedTypedChars) return; - mHasUncommittedTypedChars = false; + if (!mWordComposer.isComposingWord()) return; final CharSequence typedWord = mWordComposer.getTypedWord(); + mWordComposer.onCommitWord(); if (typedWord.length() > 0) { if (ic != null) { ic.commitText(typedWord, 1); @@ -1383,8 +1307,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_DUMMY); mSpaceState = SPACE_STATE_NONE; - mWordSavedForAutoCorrectCancellation = null; mEnteredText = text; + mWordComposer.reset(); } @Override @@ -1420,31 +1344,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return; } - if (mHasUncommittedTypedChars) { + if (mWordComposer.isComposingWord()) { final int length = mWordComposer.size(); if (length > 0) { mWordComposer.deleteLast(); ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); - if (mWordComposer.size() == 0) { - mHasUncommittedTypedChars = false; - // Remaining size equals zero means we just erased the last character of the - // word, so we can show bigrams. + // If we have deleted the last remaining character of a word, then we are not + // isComposingWord() any more. + if (!mWordComposer.isComposingWord()) { + // Not composing word any more, so we can show bigrams. mHandler.postUpdateBigramPredictions(); } else { - // length > 1, so we still have letters to deduce a suggestion from. + // Still composing a word, so we still have letters to deduce a suggestion from. mHandler.postUpdateSuggestions(); } } else { ic.deleteSurroundingText(1, 0); } } else { - if (null != mWordSavedForAutoCorrectCancellation) { + if (mWordComposer.didAutoCorrectToAnotherWord()) { Utils.Stats.onAutoCorrectionCancellation(); cancelAutoCorrect(ic); - mWordSavedForAutoCorrectCancellation = null; return; - } else { - mWordSavedForAutoCorrectCancellation = null; } if (SPACE_STATE_DOUBLE == spaceState) { @@ -1520,7 +1441,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic); } - boolean isComposingWord = mHasUncommittedTypedChars; + boolean isComposingWord = mWordComposer.isComposingWord(); int code = primaryCode; if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) && isSuggestionsRequested() && !isCursorTouchingWord()) { @@ -1557,7 +1478,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } if (isComposingWord) { - mHasUncommittedTypedChars = true; mWordComposer.add(code, keyCodes, x, y); if (ic != null) { // If it's the first letter, make note of auto-caps state @@ -1600,16 +1520,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (ic != null) { ic.beginBatchEdit(); } - // Reset the saved word in all cases. If this separator causes an autocorrection, - // it will overwrite this null with the actual word we need to save. - mWordSavedForAutoCorrectCancellation = null; - if (mHasUncommittedTypedChars) { + if (mWordComposer.isComposingWord()) { // In certain languages where single quote is a separator, it's better // not to auto correct, but accept the typed word. For instance, // in Italian dov' should not be expanded to dove' because the elision // requires the last vowel to be removed. final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputTypeNoAutoCorrect; + && !mInputAttributes.mInputTypeNoAutoCorrect; if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { commitCurrentAutoCorrection(primaryCode, ic); } else { @@ -1686,7 +1603,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public boolean isSuggestionsRequested() { - return mIsSettingsSuggestionStripOn + return mInputAttributes.mIsSettingsSuggestionStripOn && (mCorrectionMode > 0 || isShowingSuggestionsStrip()); } @@ -1708,7 +1625,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return true; if (!isShowingSuggestionsStrip()) return false; - if (mApplicationSpecifiedCompletionOn) + if (mInputAttributes.mApplicationSpecifiedCompletionOn) return true; return isSuggestionsRequested(); } @@ -1778,7 +1695,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.cancelUpdateSuggestions(); mHandler.cancelUpdateBigramPredictions(); - if (!mHasUncommittedTypedChars) { + if (!mWordComposer.isComposingWord()) { setPunctuationSuggestions(); return; } @@ -1793,9 +1710,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } // getSuggestedWordBuilder handles gracefully a null value of prevWord final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer, - prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode); + prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode); - boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); + boolean autoCorrectionAvailable = !mInputAttributes.mInputTypeNoAutoCorrect + && mSuggest.hasAutoCorrection(); final CharSequence typedWord = mWordComposer.getTypedWord(); // Here, we want to promote a whitelisted word if exists. // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" @@ -1885,9 +1803,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); mExpectingUpdateSelection = true; commitBestWord(autoCorrection); - if (!autoCorrection.equals(typedWord)) { - mWordSavedForAutoCorrectCancellation = autoCorrection.toString(); - } // Add the word to the user unigram dictionary if it's not a known word addToUserUnigramAndBigramDictionaries(autoCorrection, UserUnigramDictionary.FREQUENCY_FOR_TYPED); @@ -1911,7 +1826,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (ic != null) { ic.beginBatchEdit(); } - if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null + if (mInputAttributes.mApplicationSpecifiedCompletionOn + && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { if (ic != null) { final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; @@ -1940,7 +1856,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // pressed space on purpose of displaying the suggestion strip punctuation. final int rawPrimaryCode = suggestion.charAt(0); // Maybe apply the "bidi mirrored" conversions for parentheses - final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); + final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final boolean isRtl = keyboard != null && keyboard.mIsRtlKeyboard; final int primaryCode = Key.getRtlParenthesisCode(rawPrimaryCode, isRtl); @@ -1951,11 +1867,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } return; } - if (!mHasUncommittedTypedChars) { - // If we are not composing a word, then it was a suggestion inferred from - // context - no user input. We should reset the word composer. - mWordComposer.reset(); - } mExpectingUpdateSelection = true; commitBestWord(suggestion); // Add the word to the auto dictionary if it's not a known word @@ -1965,12 +1876,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { addToOnlyBigramDictionary(suggestion, 1); } - // TODO: the following is fishy, because if !mHasUncommittedTypedChars we are - // going to log an empty string + // TODO: the following is fishy, because it seems there may be cases where we are not + // composing a word at all. Maybe throw an exception if !mWordComposer.isComposingWord() ? LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), suggestion.toString(), index, suggestions.mWords); // Follow it with a space - if (mInsertSpaceOnPickSuggestionManually) { + if (mInputAttributes.mInsertSpaceOnPickSuggestionManually) { sendMagicSpace(); } @@ -2031,7 +1942,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar ic.commitText(bestWord, 1); } } - mHasUncommittedTypedChars = false; + mWordComposer.onCommitWord(); } private static final WordComposer sEmptyWordComposer = new WordComposer(); @@ -2047,7 +1958,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(), mSettingsValues.mWordSeparators); SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(sEmptyWordComposer, - prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode); + prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode); if (builder.size() > 0) { // Explicitly supply an empty typed word (the no-second-arg version of @@ -2173,8 +2084,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // "ic" must not be null private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic, final CharSequence word) { - mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard()); - mHasUncommittedTypedChars = true; + mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); mComposingStateManager.onStartComposingText(); ic.deleteSurroundingText(word.length(), 0); ic.setComposingText(word, 1); @@ -2183,28 +2093,32 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // "ic" must not be null private void cancelAutoCorrect(final InputConnection ic) { - final int cancelLength = mWordSavedForAutoCorrectCancellation.length(); + mWordComposer.resumeSuggestionOnKeptWord(); + final String originallyTypedWord = mWordComposer.getTypedWord(); + final CharSequence autoCorrectedTo = mWordComposer.getAutoCorrectionOrNull(); + final int cancelLength = autoCorrectedTo.length(); final CharSequence separator = ic.getTextBeforeCursor(1, 0); if (DEBUG) { final String wordBeforeCursor = ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength) .toString(); - if (!mWordSavedForAutoCorrectCancellation.equals(wordBeforeCursor)) { + if (!autoCorrectedTo.equals(wordBeforeCursor)) { throw new RuntimeException("cancelAutoCorrect check failed: we thought we were " - + "reverting \"" + mWordSavedForAutoCorrectCancellation + + "reverting \"" + autoCorrectedTo + "\", but before the cursor we found \"" + wordBeforeCursor + "\""); } - if (mWordComposer.getTypedWord().equals(wordBeforeCursor)) { + if (originallyTypedWord.equals(wordBeforeCursor)) { throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel " - + "auto correction and revert to \"" + mWordComposer.getTypedWord() + + "auto correction and revert to \"" + originallyTypedWord + "\" but we found this very string before the cursor"); } } ic.deleteSurroundingText(cancelLength + 1, 0); - + ic.commitText(originallyTypedWord, 1); // Re-insert the separator - ic.commitText(mWordComposer.getTypedWord(), 1); ic.commitText(separator, 1); + mWordComposer.deleteAutoCorrection(); + mWordComposer.onCommitWord(); Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); mHandler.cancelUpdateBigramPredictions(); @@ -2232,7 +2146,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Note: in the interest of code simplicity, we may want to just call // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving // the old WordComposer allows to reuse the actual typed coordinates. - mHasUncommittedTypedChars = true; + mWordComposer.resumeSuggestionOnKeptWord(); ic.setComposingText(mWordComposer.getTypedWord(), 1); mHandler.cancelUpdateBigramPredictions(); mHandler.postUpdateSuggestions(); @@ -2418,13 +2332,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void updateCorrectionMode() { // TODO: cleanup messy flags final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputTypeNoAutoCorrect; + && !mInputAttributes.mInputTypeNoAutoCorrect; mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE; mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect) ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode; } - private void updateSuggestionVisibility(final SharedPreferences prefs, final Resources res) { + private void updateSuggestionVisibility(final Resources res) { final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting; for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) { if (suggestionVisiblityStr.equals(res.getString(visibility))) { @@ -2514,17 +2428,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final Printer p = new PrintWriterPrinter(fout); p.println("LatinIME state :"); - final Keyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); + final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); - p.println(" mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn); + p.println(" mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn); p.println(" mCorrectionMode=" + mCorrectionMode); - p.println(" mHasUncommittedTypedChars=" + mHasUncommittedTypedChars); + p.println(" isComposingWord=" + mWordComposer.isComposingWord()); p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled); - p.println(" mInsertSpaceOnPickSuggestionManually=" + mInsertSpaceOnPickSuggestionManually); - p.println(" mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn); p.println(" mSoundOn=" + mSettingsValues.mSoundOn); p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn); p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn); + p.println(" mInputAttributes=" + mInputAttributes.toString()); } } diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index da5058dd4..6f1adfe71 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -29,6 +29,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static boolean sDBG = false; public static boolean sVISUALDEBUG = false; + public static boolean sUsabilityStudy = false; @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 0ad1c1529..651d90ca4 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -52,7 +52,9 @@ public class SettingsValues { private final String mVoiceMode; private final String mAutoCorrectionThresholdRawValue; public final String mShowSuggestionsSetting; + @SuppressWarnings("unused") // TODO: Use this private final boolean mUsabilityStudyMode; + @SuppressWarnings("unused") // TODO: Use this private final String mKeyPreviewPopupDismissDelayRawValue; public final boolean mUseContactsDict; // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary @@ -60,7 +62,9 @@ public class SettingsValues { // Prediction: use bigrams to predict the next word when there is no input for it yet public final boolean mBigramPredictionEnabled; public final boolean mEnableSuggestionSpanInsertion; + @SuppressWarnings("unused") // TODO: Use this private final int mVibrationDurationSettingsRawValue; + @SuppressWarnings("unused") // TODO: Use this private final float mKeypressSoundVolumeRawValue; // Deduced settings @@ -111,12 +115,12 @@ public class SettingsValues { res.getString(R.string.auto_correction_threshold_mode_index_modest)); mShowSuggestionsSetting = prefs.getString(Settings.PREF_SHOW_SUGGESTIONS_SETTING, res.getString(R.string.prefs_suggestion_visibility_default_value)); - mUsabilityStudyMode = getUsabilityStudyMode(prefs, res); + mUsabilityStudyMode = getUsabilityStudyMode(prefs); mKeyPreviewPopupDismissDelayRawValue = prefs.getString( Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, Integer.toString(res.getInteger(R.integer.config_delay_after_preview))); mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); - mAutoCorrectEnabled = isAutoCorrectEnabled(prefs, res, mAutoCorrectionThresholdRawValue); + mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue); mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled); mBigramPredictionEnabled = mBigramSuggestionEnabled @@ -131,7 +135,7 @@ public class SettingsValues { mKeypressVibrationDuration = getCurrentVibrationDuration(prefs, res); mFxVolume = getCurrentKeypressSoundVolume(prefs, res); mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res); - mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res, + mAutoCorrectionThreshold = getAutoCorrectionThreshold(res, mAutoCorrectionThresholdRawValue); mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff); mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); @@ -202,8 +206,8 @@ public class SettingsValues { return mMagicSpaceSwappers.contains(String.valueOf((char)code)); } - private static boolean isAutoCorrectEnabled(final SharedPreferences sp, - final Resources resources, final String currentAutoCorrectionSetting) { + private static boolean isAutoCorrectEnabled(final Resources resources, + final String currentAutoCorrectionSetting) { final String autoCorrectionOff = resources.getString( R.string.auto_correction_threshold_mode_index_off); return !currentAutoCorrectionSetting.equals(autoCorrectionOff); @@ -244,8 +248,8 @@ public class SettingsValues { R.bool.config_default_bigram_prediction)); } - private static double getAutoCorrectionThreshold(final SharedPreferences sp, - final Resources resources, final String currentAutoCorrectionSetting) { + private static double getAutoCorrectionThreshold(final Resources resources, + final String currentAutoCorrectionSetting) { final String[] autoCorrectionThresholdValues = resources.getStringArray( R.array.auto_correction_threshold_values); // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off. @@ -321,8 +325,7 @@ public class SettingsValues { } // Likewise - public static boolean getUsabilityStudyMode(final SharedPreferences prefs, - final Resources res) { + public static boolean getUsabilityStudyMode(final SharedPreferences prefs) { // TODO: use mUsabilityStudyMode instead of reading it again here return prefs.getBoolean(Settings.PREF_USABILITY_STUDY_MODE, true); } diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java index 6af20c754..a7f57ae46 100644 --- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java @@ -172,7 +172,7 @@ public class UserUnigramDictionary extends ExpandableDictionary { // Nothing pending? Return if (mPendingWrites.isEmpty()) return; // Create a background thread to write the pending entries - new UpdateDbTask(getContext(), sOpenHelper, mPendingWrites, mLocale).execute(); + new UpdateDbTask(sOpenHelper, mPendingWrites, mLocale).execute(); // Create a new map for writing new entries into while the old one is written to db mPendingWrites = new HashMap<String, Integer>(); } @@ -227,8 +227,8 @@ public class UserUnigramDictionary extends ExpandableDictionary { private final DatabaseHelper mDbHelper; private final String mLocale; - public UpdateDbTask(@SuppressWarnings("unused") Context context, DatabaseHelper openHelper, - HashMap<String, Integer> pendingWrites, String locale) { + public UpdateDbTask(DatabaseHelper openHelper, HashMap<String, Integer> pendingWrites, + String locale) { mMap = pendingWrites; mLocale = locale; mDbHelper = openHelper; diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 60a9685bc..54d908011 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,10 +16,11 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.keyboard.Keyboard; +import android.text.TextUtils; + import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; -import com.android.inputmethod.keyboard.LatinKeyboard; +import com.android.inputmethod.keyboard.Keyboard; import java.util.ArrayList; import java.util.Arrays; @@ -41,12 +42,14 @@ public class WordComposer { int[] mXCoordinates; int[] mYCoordinates; StringBuilder mTypedWord; + CharSequence mAutoCorrection; CharacterStore() { final int N = BinaryDictionary.MAX_WORD_LENGTH; mCodes = new ArrayList<int[]>(N); mTypedWord = new StringBuilder(N); mXCoordinates = new int[N]; mYCoordinates = new int[N]; + mAutoCorrection = null; } CharacterStore(final CharacterStore that) { mCodes = new ArrayList<int[]>(that.mCodes); @@ -58,16 +61,14 @@ public class WordComposer { // For better performance than creating a new character store. mCodes.clear(); mTypedWord.setLength(0); + mAutoCorrection = null; } } - // The currently typing word. - // NOTE: this is not reset as soon as the word is committed because it may be needed again - // to resume suggestion if backspaced. TODO: separate cleanly what is actually being - // composed and what is kept for possible resuming. + // The currently typing word. May not be null. private CharacterStore mCurrentWord; - // An auto-correction for this word out of the dictionary. - private CharSequence mAutoCorrection; + // The information being kept for resuming suggestion. May be null if wiped. + private CharacterStore mCommittedWordSavedForSuggestionResuming; private int mCapsCount; @@ -82,8 +83,8 @@ public class WordComposer { public WordComposer() { mCurrentWord = new CharacterStore(); + mCommittedWordSavedForSuggestionResuming = null; mTrailingSingleQuotesCount = 0; - mAutoCorrection = null; } public WordComposer(WordComposer source) { @@ -92,11 +93,11 @@ public class WordComposer { public void init(WordComposer source) { mCurrentWord = new CharacterStore(source.mCurrentWord); + mCommittedWordSavedForSuggestionResuming = source.mCommittedWordSavedForSuggestionResuming; mCapsCount = source.mCapsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; mAutoCapitalized = source.mAutoCapitalized; mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; - mAutoCorrection = null; } /** @@ -104,10 +105,10 @@ public class WordComposer { */ public void reset() { mCurrentWord.reset(); + mCommittedWordSavedForSuggestionResuming = null; mCapsCount = 0; mIsFirstCharCapitalized = false; mTrailingSingleQuotesCount = 0; - mAutoCorrection = null; } /** @@ -118,6 +119,10 @@ public class WordComposer { return mCurrentWord.mTypedWord.length(); } + public final boolean isComposingWord() { + return size() > 0; + } + /** * Returns the codes at a particular position in the word. * @param index the position in the word @@ -162,13 +167,13 @@ public class WordComposer { } else { mTrailingSingleQuotesCount = 0; } - mAutoCorrection = null; + mCurrentWord.mAutoCorrection = null; } /** * Internal method to retrieve reasonable proximity info for a character. */ - private void addKeyInfo(final int codePoint, final LatinKeyboard keyboard, + private void addKeyInfo(final int codePoint, final Keyboard keyboard, final KeyDetector keyDetector) { for (final Key key : keyboard.mKeys) { if (key.mCode == codePoint) { @@ -188,7 +193,7 @@ public class WordComposer { * Set the currently composing word to the one passed as an argument. * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. */ - public void setComposingWord(final CharSequence word, final LatinKeyboard keyboard, + public void setComposingWord(final CharSequence word, final Keyboard keyboard, final KeyDetector keyDetector) { reset(); final int length = word.length(); @@ -196,13 +201,13 @@ public class WordComposer { int codePoint = word.charAt(i); addKeyInfo(codePoint, keyboard, keyDetector); } - mAutoCorrection = null; + mCommittedWordSavedForSuggestionResuming = null; } /** * Shortcut for the above method, this will create a new KeyDetector for the passed keyboard. */ - public void setComposingWord(final CharSequence word, final LatinKeyboard keyboard) { + public void setComposingWord(final CharSequence word, final Keyboard keyboard) { final KeyDetector keyDetector = new KeyDetector(0); keyDetector.setKeyboard(keyboard, 0, 0); keyDetector.setProximityCorrectionEnabled(true); @@ -248,7 +253,7 @@ public class WordComposer { ++mTrailingSingleQuotesCount; } } - mAutoCorrection = null; + mCurrentWord.mAutoCorrection = null; } /** @@ -307,20 +312,44 @@ public class WordComposer { * Sets the auto-correction for this word. */ public void setAutoCorrection(final CharSequence correction) { - mAutoCorrection = correction; + mCurrentWord.mAutoCorrection = correction; } /** * Remove any auto-correction that may have been set. */ public void deleteAutoCorrection() { - mAutoCorrection = null; + mCurrentWord.mAutoCorrection = null; } /** * @return the auto-correction for this word, or null if none. */ public CharSequence getAutoCorrectionOrNull() { - return mAutoCorrection; + return mCurrentWord.mAutoCorrection; + } + + // TODO: pass the information about what was committed and how. Was it an auto-correction? + // Was it a completion? Was is what the user typed? + public void onCommitWord() { + mCommittedWordSavedForSuggestionResuming = mCurrentWord; + // TODO: improve performance by swapping buffers instead of creating a new object. + mCurrentWord = new CharacterStore(); + } + + public boolean hasWordKeptForSuggestionResuming() { + return null != mCommittedWordSavedForSuggestionResuming; + } + + public void resumeSuggestionOnKeptWord() { + mCurrentWord = mCommittedWordSavedForSuggestionResuming; + mCommittedWordSavedForSuggestionResuming = null; + } + + public boolean didAutoCorrectToAnotherWord() { + return null != mCommittedWordSavedForSuggestionResuming + && !TextUtils.isEmpty(mCommittedWordSavedForSuggestionResuming.mAutoCorrection) + && !TextUtils.equals(mCommittedWordSavedForSuggestionResuming.mTypedWord, + mCommittedWordSavedForSuggestionResuming.mAutoCorrection); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java index 170be347b..d747a024c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java +++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.keyboard.internal; +package com.android.inputmethod.latin; import android.content.res.TypedArray; @@ -47,14 +47,14 @@ public class XmlParseUtils { } @SuppressWarnings("serial") - static class IllegalAttribute extends ParseException { + public static class IllegalAttribute extends ParseException { public IllegalAttribute(XmlPullParser parser, String attribute) { super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser); } } @SuppressWarnings("serial") - static class NonEmptyTag extends ParseException{ + public static class NonEmptyTag extends ParseException{ public NonEmptyTag(String tag, XmlPullParser parser) { super(tag + " must be empty tag", parser); } diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 86072b64b..3d26d972d 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.suggestions; import android.content.res.Resources; import android.graphics.Paint; @@ -25,26 +25,27 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.keyboard.internal.KeyboardBuilder; -import com.android.inputmethod.keyboard.internal.KeyboardParams; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; public class MoreSuggestions extends Keyboard { - private static final boolean DBG = LatinImeLogger.sDBG; - public static final int SUGGESTION_CODE_BASE = 1024; private MoreSuggestions(Builder.MoreSuggestionsParam params) { super(params); } - public static class Builder extends KeyboardBuilder<Builder.MoreSuggestionsParam> { + public static class Builder extends Keyboard.Builder<Builder.MoreSuggestionsParam> { + private static final boolean DBG = LatinImeLogger.sDBG; + private final MoreSuggestionsView mPaneView; private SuggestedWords mSuggestions; private int mFromPos; private int mToPos; - public static class MoreSuggestionsParam extends KeyboardParams { + public static class MoreSuggestionsParam extends Keyboard.Params { private final int[] mWidths = new int[SuggestionsView.MAX_SUGGESTIONS]; private final int[] mRowNumbers = new int[SuggestionsView.MAX_SUGGESTIONS]; private final int[] mColumnOrders = new int[SuggestionsView.MAX_SUGGESTIONS]; @@ -176,9 +177,9 @@ public class MoreSuggestions extends Keyboard { public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth, int minWidth, int maxRow) { - final Keyboard keyboard = KeyboardSwitcher.getInstance().getLatinKeyboard(); + final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard(); final int xmlId = R.xml.kbd_suggestions_pane_template; - load(keyboard.mId.cloneWithNewXml(xmlId)); + load(xmlId, keyboard.mId); mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2; final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow, diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java index c61dd6313..b5f67ace0 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; @@ -34,6 +34,7 @@ import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.latin.R; /** * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java index dbd4677f0..40d782640 100644 --- a/java/src/com/android/inputmethod/latin/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; @@ -57,7 +57,12 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.Utils; import java.util.ArrayList; import java.util.List; @@ -72,7 +77,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. public static final int MAX_SUGGESTIONS = 18; - private static final boolean DBG = LatinImeLogger.sDBG; + static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mSuggestionsStrip; private KeyboardView mKeyboardView; @@ -100,8 +105,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> { private static final int MSG_HIDE_PREVIEW = 0; - private static final long DELAY_HIDE_PREVIEW = 1300; - public UiHandler(SuggestionsView outerInstance) { super(outerInstance); } @@ -116,11 +119,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, } } - public void postHidePreview() { - cancelHidePreview(); - sendMessageDelayed(obtainMessage(MSG_HIDE_PREVIEW), DELAY_HIDE_PREVIEW); - } - public void cancelHidePreview() { removeMessages(MSG_HIDE_PREVIEW); } @@ -148,6 +146,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private final List<View> mDividers; private final List<TextView> mInfos; + private final int mColorValidTypedWord; private final int mColorTypedWord; private final int mColorAutoCorrect; private final int mColorSuggested; @@ -191,6 +190,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle); mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0); + final float alphaValidTypedWord = getPercent(a, + R.styleable.SuggestionsView_alphaValidTypedWord, 100); final float alphaTypedWord = getPercent(a, R.styleable.SuggestionsView_alphaTypedWord, 100); final float alphaAutoCorrect = getPercent(a, @@ -198,6 +199,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final float alphaSuggested = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100); mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100); + mColorValidTypedWord = applyAlpha( + a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0), + alphaValidTypedWord); mColorTypedWord = applyAlpha( a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord); mColorAutoCorrect = applyAlpha( @@ -295,6 +299,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final int color; if (index == mCenterSuggestionIndex && Utils.willAutoCorrect(suggestions)) { color = mColorAutoCorrect; + } else if (index == mCenterSuggestionIndex && suggestions.mTypedWordValid) { + color = mColorValidTypedWord; } else if (isSuggested) { color = mColorSuggested; } else { @@ -430,7 +436,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final TextView word = mWords.get(index); word.setEnabled(true); - word.setTextColor(mColorTypedWord); + word.setTextColor(mColorAutoCorrect); final CharSequence text = suggestions.getWord(index); word.setText(text); word.setTextScaleX(1.0f); |