diff options
125 files changed, 3478 insertions, 1032 deletions
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 631c35d87..2a5334f95 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -388,6 +388,7 @@ <enum name="alphabetShiftLocked" value="3" /> <enum name="alphabetShiftLockShifted" value="4" /> <enum name="symbols" value="5" /> + <enum name="symbolsShifted" value="6" /> <enum name="phone" value="7" /> <enum name="phoneSymbols" value="8" /> <enum name="number" value="9" /> @@ -449,6 +450,7 @@ <enum name="alphabetShiftLocked" value="3" /> <enum name="alphabetShiftLockShifted" value="4" /> <enum name="symbols" value="5" /> + <enum name="symbolsShifted" value="6" /> <enum name="phone" value="7" /> <enum name="phoneSymbols" value="8" /> <enum name="number" value="9" /> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index 599af124c..7de93e6e3 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -122,4 +122,6 @@ <dimen name="accessibility_edge_slop">8dp</dimen> <integer name="user_dictionary_max_word_length" translatable="false">48</integer> + + <dimen name="language_on_spacebar_horizontal_margin">1dp</dimen> </resources> diff --git a/java/res/xml-sw600dp/key_space.xml b/java/res/xml-sw600dp/key_space_5kw.xml index 86af89f50..86af89f50 100644 --- a/java/res/xml-sw600dp/key_space.xml +++ b/java/res/xml-sw600dp/key_space_5kw.xml diff --git a/java/res/xml-sw600dp/row_symbols4.xml b/java/res/xml-sw600dp/key_space_symbols.xml index 26ce1ebf4..07aa7d179 100644 --- a/java/res/xml-sw600dp/row_symbols4.xml +++ b/java/res/xml-sw600dp/key_space_symbols.xml @@ -21,20 +21,6 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Row - latin:keyWidth="9.0%p" - latin:backgroundType="functional" - > - <Key - latin:keyStyle="toAlphaKeyStyle" - latin:keyWidth="10.0%p" /> - <include - latin:keyXPos="28.0%p" - latin:keyboardLayout="@xml/key_space" - latin:backgroundType="normal" /> - <Spacer /> - <Spacer /> - <include - latin:keyboardLayout="@xml/key_f2" /> - </Row> + <include + latin:keyboardLayout="@xml/key_space_5kw" /> </merge> diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index 3b20281be..d817add11 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -179,6 +179,16 @@ latin:keyLabel="!text/label_to_alpha_key" latin:parentStyle="baseForLayoutSwitchKeyStyle" /> <key-style + latin:styleName="toMoreSymbolKeyStyle" + latin:code="!code/key_shift" + latin:keyLabel="!text/label_to_more_symbol_for_tablet_key" + latin:parentStyle="baseForLayoutSwitchKeyStyle" /> + <key-style + latin:styleName="backFromMoreSymbolKeyStyle" + latin:code="!code/key_shift" + latin:keyLabel="!text/label_to_symbol_key" + latin:parentStyle="baseForLayoutSwitchKeyStyle" /> + <key-style latin:styleName="comKeyStyle" latin:keyLabel="!text/keylabel_for_popular_domain" latin:keyLabelFlags="autoXScale|fontNormal|hasPopupHint|preserveCase" diff --git a/java/res/xml-sw600dp/row_dvorak4.xml b/java/res/xml-sw600dp/row_dvorak4.xml index 1dfb3f462..11b403452 100644 --- a/java/res/xml-sw600dp/row_dvorak4.xml +++ b/java/res/xml-sw600dp/row_dvorak4.xml @@ -34,7 +34,7 @@ latin:keyboardLayout="@xml/key_f1" /> <include latin:keyXPos="28.0%p" - latin:keyboardLayout="@xml/key_space" + latin:keyboardLayout="@xml/key_space_5kw" latin:backgroundType="normal" /> <include latin:keyboardLayout="@xml/key_question_exclamation" /> diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml index 16d373108..7969dd8a5 100644 --- a/java/res/xml-sw600dp/row_qwerty4.xml +++ b/java/res/xml-sw600dp/row_qwerty4.xml @@ -34,7 +34,7 @@ latin:keyboardLayout="@xml/key_f1" /> <include latin:keyXPos="28.0%p" - latin:keyboardLayout="@xml/key_space" + latin:keyboardLayout="@xml/key_space_5kw" latin:backgroundType="normal" /> <include latin:keyboardLayout="@xml/keys_comma_period" /> diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml index dee79e64f..fbd8492cd 100644 --- a/java/res/xml-sw600dp/rows_symbols.xml +++ b/java/res/xml-sw600dp/rows_symbols.xml @@ -23,6 +23,8 @@ > <include latin:keyboardLayout="@xml/key_styles_common" /> + <include + latin:keyboardLayout="@xml/key_styles_currency" /> <Row latin:keyWidth="9.0%p" > @@ -36,23 +38,35 @@ latin:keyWidth="9.0%p" > <include - latin:keyboardLayout="@xml/rowkeys_symbols2" /> + latin:keyboardLayout="@xml/rowkeys_symbols2" + latin:keyXPos="4.5%p" /> <Key latin:keyStyle="enterKeyStyle" latin:keyWidth="fillRight" /> </Row> - <Row + <Row latin:keyWidth="9.0%p" > + <Key + latin:keyStyle="toMoreSymbolKeyStyle" + latin:keyWidth="10.0%p" /> + <Key + latin:keyLabel="\\" /> + <Key + latin:keyLabel="=" /> <include - latin:keyXPos="1.0%p" latin:keyboardLayout="@xml/rowkeys_symbols3" /> <Key - latin:keyLabel="," /> + latin:keyStyle="toMoreSymbolKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="9.0%p" + > + <Key + latin:keyStyle="toAlphaKeyStyle" + latin:keyWidth="10.0%p" /> <include - latin:keyboardLayout="@xml/key_symbols_period" - latin:backgroundType="normal" /> + latin:keyboardLayout="@xml/row_symbols4" /> </Row> - <include - latin:keyboardLayout="@xml/row_symbols4" /> </merge> diff --git a/java/res/xml-sw600dp/rows_symbols_shift.xml b/java/res/xml-sw600dp/rows_symbols_shift.xml new file mode 100644 index 000000000..aad047ff6 --- /dev/null +++ b/java/res/xml-sw600dp/rows_symbols_shift.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <include + latin:keyboardLayout="@xml/key_styles_currency" /> + <Row + latin:keyWidth="9.0%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_symbols_shift1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="9.0%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_symbols_shift2" + latin:keyXPos="4.5%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="9.0%p" + > + <Key + latin:keyStyle="backFromMoreSymbolKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_symbols_shift3" /> + <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK --> + <Key + latin:keyLabel="¡" /> + <!-- U+00BF: "¿" INVERTED QUESTION MARK --> + <Key + latin:keyLabel="¿" /> + <Key + latin:keyStyle="backFromMoreSymbolKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="9.0%p" + > + <Key + latin:keyStyle="toAlphaKeyStyle" + latin:keyWidth="10%p" /> + <include + latin:keyboardLayout="@xml/row_symbols_shift4" /> + </Row> +</merge> diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml new file mode 100644 index 000000000..932ec017e --- /dev/null +++ b/java/res/xml/kbd_symbols_shift.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<Keyboard + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" +> + <include + latin:keyboardLayout="@xml/rows_symbols_shift" /> +</Keyboard> diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_thai_symbols_shift.xml index 4d9861b73..a2d67caf4 100644 --- a/java/res/xml/kbd_10_10_7_symbols.xml +++ b/java/res/xml/kbd_thai_symbols_shift.xml @@ -23,5 +23,5 @@ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" > <include - latin:keyboardLayout="@xml/rows_symbols" /> + latin:keyboardLayout="@xml/rows_symbols_shift" /> </Keyboard> diff --git a/java/res/xml/key_space_3kw.xml b/java/res/xml/key_space_3kw.xml new file mode 100644 index 000000000..20ec882df --- /dev/null +++ b/java/res/xml/key_space_3kw.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="20%p" /> + </case> + <!-- languageSwitchKeyEnabled="false" --> + <default> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="30%p" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/key_space.xml b/java/res/xml/key_space_5kw.xml index 02ee42fd2..02ee42fd2 100644 --- a/java/res/xml/key_space.xml +++ b/java/res/xml/key_space_5kw.xml diff --git a/java/res/xml/key_space_symbols.xml b/java/res/xml/key_space_symbols.xml new file mode 100644 index 000000000..1efc4ff4e --- /dev/null +++ b/java/res/xml/key_space_symbols.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_space_3kw" /> +</merge> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index c1c2c3e06..6b3dc9a0d 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -21,6 +21,20 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted" + > + <key-style + latin:styleName="hasShiftedLetterHintStyle" + latin:keyLabelFlags="hasShiftedLetterHint|shiftedLetterActivated" /> + </case> + <default> + <key-style + latin:styleName="hasShiftedLetterHintStyle" + latin:keyLabelFlags="hasShiftedLetterHint" /> + </default> + </switch> <!-- Base key style for the key which may have settings or tab key as popup key. --> <include latin:keyboardLayout="@xml/key_styles_f1" /> @@ -156,6 +170,15 @@ latin:keyLabel="!text/label_to_alpha_key" latin:parentStyle="baseForLayoutSwitchKeyStyle" /> <key-style + latin:styleName="toMoreSymbolKeyStyle" + latin:code="!code/key_shift" + latin:keyLabel="!text/label_to_more_symbol_key" + latin:parentStyle="baseForLayoutSwitchKeyStyle" /> + <key-style + latin:styleName="backFromMoreSymbolKeyStyle" + latin:code="!code/key_shift" + latin:parentStyle="baseForToSymbolKeyStyle" /> + <key-style latin:styleName="punctuationKeyStyle" latin:keyLabel="." latin:keyLabelFlags="hasPopupHint" diff --git a/java/res/xml/key_currency.xml b/java/res/xml/key_styles_currency.xml index 2e4f828ce..60333eeb4 100644 --- a/java/res/xml/key_currency.xml +++ b/java/res/xml/key_styles_currency.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ */ --> -<!-- TODO: Move these definitions to text resources and remove key_currency.xml. --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > @@ -27,7 +26,7 @@ latin:passwordInput="true" > <include - latin:keyboardLayout="@xml/key_dollar" /> + latin:keyboardLayout="@xml/key_styles_currency_dollar" /> </case> <!-- Countries using Euro currency, 23 countries as of November 2012. 1. Andorra (ca_AD, ca_ES) @@ -57,7 +56,7 @@ latin:countryCode="AD|AT|BE|CY|EE|FI|FR|DE|GR|IE|IT|XK|LU|MT|MO|ME|NL|PT|SM|SK|SI|ES|VA" > <include - latin:keyboardLayout="@xml/key_euro" /> + latin:keyboardLayout="@xml/key_styles_currency_euro" /> </case> <!-- Note: Some subtype locale may not have country code, and it it supposed to indicate the country where the language originally/mainly spoken. --> @@ -81,7 +80,7 @@ latin:localeCode="da|de|es|el|fi|fr|it|nl|sk|sl|sv|tr" > <include - latin:keyboardLayout="@xml/key_euro" /> + latin:keyboardLayout="@xml/key_styles_currency_euro" /> </case> <!-- ca: Catalan (Andorra, Spain) et: Estonian (Estonia) @@ -91,7 +90,7 @@ latin:languageCode="ca|et|lb|mt" > <include - latin:keyboardLayout="@xml/key_euro" /> + latin:keyboardLayout="@xml/key_styles_currency_euro" /> </case> <!-- fa: Persian (Rial and Afgahni) hi: Hindi (Indian Rupee) @@ -105,9 +104,26 @@ <case latin:languageCode="fa|hi|iw|mn|th|uk|vi" > - <Key + <!-- U+00A3: "£" POUND SIGN + U+20AC: "€" EURO SIGN + U+00A2: "¢" CENT SIGN --> + <key-style + latin:styleName="currencyKeyStyle" latin:keyLabel="!text/keylabel_for_currency" latin:moreKeys="!text/more_keys_for_currency" /> + <key-style + latin:styleName="moreCurrency1KeyStyle" + latin:keyLabel="£" /> + <key-style + latin:styleName="moreCurrency2KeyStyle" + latin:keyLabel="€" /> + <key-style + latin:styleName="moreCurrency3KeyStyle" + latin:keyLabel="$" + latin:moreKeys="¢" /> + <key-style + latin:styleName="moreCurrency4KeyStyle" + latin:keyLabel="¢" /> </case> <!-- GB: United Kingdom (Pound) --> <case @@ -118,14 +134,28 @@ U+00A5: "¥" YEN SIGN U+00A2: "¢" CENT SIGN U+20B1: "₱" PESO SIGN --> - <Key + <key-style + latin:styleName="currencyKeyStyle" latin:keyLabel="£" latin:moreKeys="¢,$,€,¥,₱" /> + <key-style + latin:styleName="moreCurrency1KeyStyle" + latin:keyLabel="€" /> + <key-style + latin:styleName="moreCurrency2KeyStyle" + latin:keyLabel="¥" /> + <key-style + latin:styleName="moreCurrency3KeyStyle" + latin:keyLabel="$" + latin:moreKeys="¢" /> + <key-style + latin:styleName="moreCurrency4KeyStyle" + latin:keyLabel="¢" /> </case> <!-- ar: Arabic (Dollar and Rial) --> <default> <include - latin:keyboardLayout="@xml/key_dollar" /> + latin:keyboardLayout="@xml/key_styles_currency_dollar" /> </default> </switch> </merge> diff --git a/java/res/xml/key_dollar.xml b/java/res/xml/key_styles_currency_dollar.xml index 118c7a291..674a3966d 100644 --- a/java/res/xml/key_dollar.xml +++ b/java/res/xml/key_styles_currency_dollar.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -23,7 +23,20 @@ U+00A2: "¢" CENT SIGN U+20AC: "€" EURO SIGN U+00A5: "¥" YEN SIGN --> - <Key + <key-style + latin:styleName="currencyKeyStyle" latin:keyLabel="$" latin:moreKeys="!text/more_keys_for_currency_dollar" /> + <key-style + latin:styleName="moreCurrency1KeyStyle" + latin:keyLabel="£" /> + <key-style + latin:styleName="moreCurrency2KeyStyle" + latin:keyLabel="¢" /> + <key-style + latin:styleName="moreCurrency3KeyStyle" + latin:keyLabel="€" /> + <key-style + latin:styleName="moreCurrency4KeyStyle" + latin:keyLabel="¥" /> </merge> diff --git a/java/res/xml/key_styles_currency_euro.xml b/java/res/xml/key_styles_currency_euro.xml new file mode 100644 index 000000000..c1b5e0384 --- /dev/null +++ b/java/res/xml/key_styles_currency_euro.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <!-- U+20AC: "€" EURO SIGN + U+00A2: "¢" CENT SIGN + U+00A3: "£" POUND SIGN + U+00A5: "¥" YEN SIGN + U+20B1: "₱" PESO SIGN --> + <key-style + latin:styleName="currencyKeyStyle" + latin:keyLabel="€" + latin:moreKeys="¢,£,$,¥,₱" /> + <key-style + latin:styleName="moreCurrency1KeyStyle" + latin:keyLabel="£" /> + <key-style + latin:styleName="moreCurrency2KeyStyle" + latin:keyLabel="¥" /> + <key-style + latin:styleName="moreCurrency3KeyStyle" + latin:keyLabel="$" + latin:moreKeys="¢" /> + <key-style + latin:styleName="moreCurrency4KeyStyle" + latin:keyLabel="¢" /> +</merge> diff --git a/java/res/xml/keyboard_layout_set_arabic.xml b/java/res/xml/keyboard_layout_set_arabic.xml index 9eb11dc5e..10e95bd30 100644 --- a/java/res/xml/keyboard_layout_set_arabic.xml +++ b/java/res/xml/keyboard_layout_set_arabic.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_azerty.xml b/java/res/xml/keyboard_layout_set_azerty.xml index 03d8ce72b..38797f91a 100644 --- a/java/res/xml/keyboard_layout_set_azerty.xml +++ b/java/res/xml/keyboard_layout_set_azerty.xml @@ -26,7 +26,10 @@ latin:enableProximityCharsCorrection="true" /> <Element latin:elementName="symbols" - latin:elementKeyboard="@xml/kbd_10_10_7_symbols" /> + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> diff --git a/java/res/xml/keyboard_layout_set_bulgarian.xml b/java/res/xml/keyboard_layout_set_bulgarian.xml index 0b92af8e4..c6fdff9a2 100644 --- a/java/res/xml/keyboard_layout_set_bulgarian.xml +++ b/java/res/xml/keyboard_layout_set_bulgarian.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_bulgarian_bds.xml b/java/res/xml/keyboard_layout_set_bulgarian_bds.xml index d185a85b4..a36b3bd52 100644 --- a/java/res/xml/keyboard_layout_set_bulgarian_bds.xml +++ b/java/res/xml/keyboard_layout_set_bulgarian_bds.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_colemak.xml b/java/res/xml/keyboard_layout_set_colemak.xml index fd4254291..3061872a8 100644 --- a/java/res/xml/keyboard_layout_set_colemak.xml +++ b/java/res/xml/keyboard_layout_set_colemak.xml @@ -26,7 +26,10 @@ latin:enableProximityCharsCorrection="true" /> <Element latin:elementName="symbols" - latin:elementKeyboard="@xml/kbd_10_10_7_symbols" /> + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> diff --git a/java/res/xml/keyboard_layout_set_dvorak.xml b/java/res/xml/keyboard_layout_set_dvorak.xml index 851a27117..31aeec525 100644 --- a/java/res/xml/keyboard_layout_set_dvorak.xml +++ b/java/res/xml/keyboard_layout_set_dvorak.xml @@ -26,7 +26,10 @@ latin:enableProximityCharsCorrection="true" /> <Element latin:elementName="symbols" - latin:elementKeyboard="@xml/kbd_10_10_7_symbols" /> + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> diff --git a/java/res/xml/keyboard_layout_set_east_slavic.xml b/java/res/xml/keyboard_layout_set_east_slavic.xml index 9dc284668..8d66faf8f 100644 --- a/java/res/xml/keyboard_layout_set_east_slavic.xml +++ b/java/res/xml/keyboard_layout_set_east_slavic.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_farsi.xml b/java/res/xml/keyboard_layout_set_farsi.xml index ef29e1ae8..b9a91e3a8 100644 --- a/java/res/xml/keyboard_layout_set_farsi.xml +++ b/java/res/xml/keyboard_layout_set_farsi.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_georgian.xml b/java/res/xml/keyboard_layout_set_georgian.xml index 181c99713..36d091653 100644 --- a/java/res/xml/keyboard_layout_set_georgian.xml +++ b/java/res/xml/keyboard_layout_set_georgian.xml @@ -44,6 +44,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_greek.xml b/java/res/xml/keyboard_layout_set_greek.xml index 2e4a28692..b376e4fe3 100644 --- a/java/res/xml/keyboard_layout_set_greek.xml +++ b/java/res/xml/keyboard_layout_set_greek.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_hebrew.xml b/java/res/xml/keyboard_layout_set_hebrew.xml index c8ac31c7e..d5b25b36c 100644 --- a/java/res/xml/keyboard_layout_set_hebrew.xml +++ b/java/res/xml/keyboard_layout_set_hebrew.xml @@ -26,7 +26,10 @@ latin:enableProximityCharsCorrection="true" /> <Element latin:elementName="symbols" - latin:elementKeyboard="@xml/kbd_10_10_7_symbols" /> + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> diff --git a/java/res/xml/keyboard_layout_set_hindi.xml b/java/res/xml/keyboard_layout_set_hindi.xml index 1bd3d72fb..e850c7ebc 100644 --- a/java/res/xml/keyboard_layout_set_hindi.xml +++ b/java/res/xml/keyboard_layout_set_hindi.xml @@ -44,6 +44,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_mongolian.xml b/java/res/xml/keyboard_layout_set_mongolian.xml index bbf3c0945..2d364f682 100644 --- a/java/res/xml/keyboard_layout_set_mongolian.xml +++ b/java/res/xml/keyboard_layout_set_mongolian.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_nordic.xml b/java/res/xml/keyboard_layout_set_nordic.xml index ce6fc635b..1f00f440e 100644 --- a/java/res/xml/keyboard_layout_set_nordic.xml +++ b/java/res/xml/keyboard_layout_set_nordic.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_qwerty.xml b/java/res/xml/keyboard_layout_set_qwerty.xml index 98b3582d4..821517081 100644 --- a/java/res/xml/keyboard_layout_set_qwerty.xml +++ b/java/res/xml/keyboard_layout_set_qwerty.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_qwertz.xml b/java/res/xml/keyboard_layout_set_qwertz.xml index 789dcfa62..f9e87a66f 100644 --- a/java/res/xml/keyboard_layout_set_qwertz.xml +++ b/java/res/xml/keyboard_layout_set_qwertz.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_south_slavic.xml b/java/res/xml/keyboard_layout_set_south_slavic.xml index eaeaa1c4b..36666b91b 100644 --- a/java/res/xml/keyboard_layout_set_south_slavic.xml +++ b/java/res/xml/keyboard_layout_set_south_slavic.xml @@ -28,6 +28,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml/keyboard_layout_set_spanish.xml b/java/res/xml/keyboard_layout_set_spanish.xml index 500e23a10..c454de3b4 100644 --- a/java/res/xml/keyboard_layout_set_spanish.xml +++ b/java/res/xml/keyboard_layout_set_spanish.xml @@ -26,7 +26,10 @@ latin:enableProximityCharsCorrection="true" /> <Element latin:elementName="symbols" - latin:elementKeyboard="@xml/kbd_10_10_7_symbols" /> + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> diff --git a/java/res/xml/keyboard_layout_set_thai.xml b/java/res/xml/keyboard_layout_set_thai.xml index 146b413d2..94713e3d4 100644 --- a/java/res/xml/keyboard_layout_set_thai.xml +++ b/java/res/xml/keyboard_layout_set_thai.xml @@ -44,6 +44,9 @@ latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_thai_symbols" /> <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_thai_symbols_shift" /> + <Element latin:elementName="phone" latin:elementKeyboard="@xml/kbd_phone" /> <Element diff --git a/java/res/xml-sw600dp/keys_comma_period.xml b/java/res/xml/keys_comma_period.xml index a6008390e..7e7c7282e 100644 --- a/java/res/xml-sw600dp/keys_comma_period.xml +++ b/java/res/xml/keys_comma_period.xml @@ -29,11 +29,13 @@ latin:keyLabel="." latin:keyHintLabel="_" latin:moreKeys="_" + latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> <Key latin:keyLabel="," latin:keyHintLabel="-" latin:moreKeys="-" + latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> </case> <case @@ -43,12 +45,14 @@ latin:keyLabel="!text/keylabel_for_apostrophe" latin:keyHintLabel="!text/keyhintlabel_for_apostrophe" latin:moreKeys="!text/more_keys_for_apostrophe" + latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> <Key latin:keyLabel="." latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/more_keys_for_arabic_diacritics" + latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> </case> <case @@ -59,22 +63,26 @@ latin:keyHintLabel="!text/keyhintlabel_for_apostrophe" latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/more_keys_for_apostrophe" + latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> <Key latin:keyLabel="." latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/more_keys_for_arabic_diacritics" + latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> </case> <default> <Key latin:keyLabel="." latin:keyHintLabel="!text/keyhintlabel_for_tablet_period" + latin:backgroundType="functional" latin:moreKeys="!text/more_keys_for_tablet_period" /> <Key latin:keyLabel="!text/keylabel_for_tablet_comma" latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma" + latin:backgroundType="functional" latin:moreKeys="!text/more_keys_for_tablet_comma" /> </default> </switch> diff --git a/java/res/xml/keys_less_greater.xml b/java/res/xml/keys_less_greater.xml index bc9ecdf2f..56d0727dd 100644 --- a/java/res/xml/keys_less_greater.xml +++ b/java/res/xml/keys_less_greater.xml @@ -30,20 +30,24 @@ <Key latin:keyLabel="«" latin:code="0x00BB" + latin:backgroundType="functional" latin:moreKeys="!text/more_keys_for_less_than" /> <Key latin:keyLabel="»" latin:code="0x00AB" + latin:backgroundType="functional" latin:moreKeys="!text/more_keys_for_greater_than" /> </case> <default> <Key latin:keyLabel="<" latin:code="!code/key_less_than" + latin:backgroundType="functional" latin:moreKeys="!text/more_keys_for_less_than" /> <Key latin:keyLabel=">" latin:code="!code/key_greater_than" + latin:backgroundType="functional" latin:moreKeys="!text/more_keys_for_greater_than" /> </default> </switch> diff --git a/java/res/xml/keys_parentheses.xml b/java/res/xml/keys_parentheses.xml new file mode 100644 index 000000000..25e89c930 --- /dev/null +++ b/java/res/xml/keys_parentheses.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keyLabel="(" + latin:code="!code/key_left_parenthesis" + latin:moreKeys="!text/more_keys_for_left_parenthesis" /> + <Key + latin:keyLabel=")" + latin:code="!code/key_right_parenthesis" + latin:moreKeys="!text/more_keys_for_right_parenthesis" /> +</merge> diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 2a726c438..6c36b0e89 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -42,13 +42,11 @@ android:title="@string/popup_on_keypress" android:persistent="true" android:defaultValue="@bool/config_default_key_preview_popup" /> - <ListPreference - android:key="voice_mode" + <CheckBoxPreference + android:key="pref_voice_input_key" android:title="@string/voice_input" android:persistent="true" - android:entryValues="@array/voice_input_modes_values" - android:entries="@array/voice_input_modes" - android:defaultValue="@string/voice_mode_main" /> + android:defaultValue="true" /> </PreferenceCategory> <PreferenceCategory android:title="@string/correction_category" diff --git a/java/res/xml/row_dvorak4.xml b/java/res/xml/row_dvorak4.xml index e6d487e1d..02a95acea 100644 --- a/java/res/xml/row_dvorak4.xml +++ b/java/res/xml/row_dvorak4.xml @@ -65,7 +65,7 @@ </switch> <include latin:keyXPos="25%p" - latin:keyboardLayout="@xml/key_space" /> + latin:keyboardLayout="@xml/key_space_5kw" /> <Key latin:keyLabel="z" latin:keyLabelFlags="hasPopupHint" diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml index e6a507435..340beb99b 100644 --- a/java/res/xml/row_qwerty4.xml +++ b/java/res/xml/row_qwerty4.xml @@ -31,7 +31,7 @@ latin:keyboardLayout="@xml/key_f1" /> <include latin:keyXPos="25%p" - latin:keyboardLayout="@xml/key_space" /> + latin:keyboardLayout="@xml/key_space_5kw" /> <switch> <case latin:languageCode="ar|fa" diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml index bea139aba..0bf412fff 100644 --- a/java/res/xml/row_symbols4.xml +++ b/java/res/xml/row_symbols4.xml @@ -18,39 +18,29 @@ */ --> -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <Row - latin:keyWidth="10%p" - > - <Key - latin:keyStyle="toAlphaKeyStyle" - latin:keyWidth="15%p" /> - <switch> - <case - latin:hasShortcutKey="true" - > - <Key - latin:keyStyle="shortcutKeyStyle" /> - </case> - <!-- latin:hasShortcutKey="false" --> - <default> - <Key - latin:keyLabel="!text/keylabel_for_comma" - latin:keyLabelFlags="hasPopupHint" - latin:additionalMoreKeys="!text/more_keys_for_comma" - latin:keyStyle="f1MoreKeysStyle" /> - </default> - </switch> - <include - latin:keyXPos="25%p" - latin:keyboardLayout="@xml/key_space" /> - <include - latin:keyboardLayout="@xml/key_symbols_period" - latin:backgroundType="functional" /> - <Key - latin:keyStyle="emojiKeyStyle" - latin:keyWidth="fillRight" /> - </Row> +<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > + + <Key + latin:backgroundType="functional" + latin:keyLabel="_" /> + <Key + latin:backgroundType="functional" + latin:keyLabel="/" /> + + <switch> + <case latin:hasShortcutKey="true" > + <Key latin:keyStyle="shortcutKeyStyle" /> + </case> + <!-- latin:hasShortcutKey="false" --> + <default> + </default> + </switch> + + <include latin:keyboardLayout="@xml/key_space_symbols" /> + <include latin:keyboardLayout="@xml/keys_comma_period" /> + + <Key + latin:keyStyle="emojiKeyStyle" + latin:keyWidth="fillRight" /> + </merge> diff --git a/java/res/xml/key_euro.xml b/java/res/xml/row_symbols_shift4.xml index 820ced97c..57a2ec09c 100644 --- a/java/res/xml/key_euro.xml +++ b/java/res/xml/row_symbols_shift4.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** Copyright 2012, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,14 +17,15 @@ ** limitations under the License. */ --> +<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > + + <include latin:keyboardLayout="@xml/keys_less_greater" /> + <include + latin:keyboardLayout="@xml/key_space_symbols" /> + <include latin:keyboardLayout="@xml/keys_comma_period" /> -<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> - <!-- U+20AC: "€" EURO SIGN - U+00A2: "¢" CENT SIGN - U+00A3: "£" POUND SIGN - U+00A5: "¥" YEN SIGN - U+20B1: "₱" PESO SIGN --> <Key - latin:keyLabel="€" - latin:moreKeys="¢,£,$,¥,₱" /> + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </merge> diff --git a/java/res/xml/rowkeys_symbols2.xml b/java/res/xml/rowkeys_symbols2.xml index 9d629f1c8..76cbf6259 100644 --- a/java/res/xml/rowkeys_symbols2.xml +++ b/java/res/xml/rowkeys_symbols2.xml @@ -43,41 +43,22 @@ latin:keyLabel="\#" /> </default> </switch> - <!-- TODO: Remove key_currency.xml and uncomment the following definition. --> -<!-- <Key --> -<!-- latin:keyLabel="!text/keylabel_for_currency" --> -<!-- latin:moreKeys="!text/more_keys_for_currency" /> --> - <include - latin:keyboardLayout="@xml/key_currency" /> + <Key + latin:keyStyle="currencyKeyStyle" /> <Key latin:keyLabel="!text/keylabel_for_symbols_percent" latin:moreKeys="!text/more_keys_for_symbols_percent" /> <Key latin:keyLabel="&" /> + <!-- U+2013: "–" EN DASH + U+2014: "—" EM DASH + U+00B7: "·" MIDDLE DOT --> <Key - latin:keyLabel="*" - latin:moreKeys="!text/more_keys_for_star" /> - <!-- U+00B1: "±" PLUS-MINUS SIGN - U+00D7: "×" MULTIPLICATION SIGN - U+00F7: "÷" DIVISION SIGN - U+221A: "√" SQUARE ROOT --> - <Key - latin:keyLabel="+" - latin:moreKeys="!text/more_keys_for_plus,±,×,÷,√" /> - <!-- U+221E: "∞" INFINITY - U+2264: "≤" LESS-THAN OR EQUAL TO - U+2265: "≥" GREATER-THAN EQUAL TO - U+2260: "≠" NOT EQUAL TO - U+2248: "≈" ALMOST EQUAL TO --> - <Key - latin:keyLabel="=" - latin:moreKeys="!fixedColumnOrder!5,∞,≤,≥,≠,≈" /> + latin:keyLabel="-" + latin:moreKeys="_,–,—,·" /> <Key - latin:keyLabel="(" - latin:code="!code/key_left_parenthesis" - latin:moreKeys="!text/more_keys_for_left_parenthesis" /> - <Key - latin:keyLabel=")" - latin:code="!code/key_right_parenthesis" - latin:moreKeys="!text/more_keys_for_right_parenthesis" /> + latin:keyLabel="+" + latin:moreKeys="!text/more_keys_for_plus" /> + <include + latin:keyboardLayout="@xml/keys_parentheses" /> </merge> diff --git a/java/res/xml/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml index 9e9dfd8bd..9f5e620e6 100644 --- a/java/res/xml/rowkeys_symbols3.xml +++ b/java/res/xml/rowkeys_symbols3.xml @@ -22,11 +22,8 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <Key - latin:keyLabel="!" - latin:moreKeys="!text/more_keys_for_symbols_exclamation" /> - <Key - latin:keyLabel="!text/keylabel_for_symbols_question" - latin:moreKeys="!text/more_keys_for_symbols_question" /> + latin:keyLabel="*" + latin:moreKeys="!text/more_keys_for_star" /> <switch> <case latin:languageCode="fa" @@ -36,11 +33,11 @@ <Key latin:keyLabel="«" latin:code="0x00BB" - latin:moreKeys="!fixedColumnOrder!6,!text/more_keys_for_double_quote,"" /> + latin:moreKeys="!text/more_keys_for_double_quote" /> <Key latin:keyLabel="»" latin:code="0x00AB" - latin:moreKeys="!fixedColumnOrder!6,!text/more_keys_for_single_quote,\'" /> + latin:moreKeys="!text/more_keys_for_single_quote" /> </case> <default> <Key @@ -51,18 +48,15 @@ latin:moreKeys="!text/more_keys_for_single_quote" /> </default> </switch> - <!-- U+2013: "–" EN DASH - U+2014: "—" EM DASH - U+00B7: "·" MIDDLE DOT --> - <Key - latin:keyLabel="-" - latin:moreKeys="_,~,–,—,·" /> <Key latin:keyLabel=":" /> <Key latin:keyLabel="!text/keylabel_for_symbols_semicolon" latin:moreKeys="!text/more_keys_for_symbols_semicolon" /> <Key - latin:keyLabel="/" - latin:moreKeys="\\|,\\\\,^" /> + latin:keyLabel="!text/keylabel_for_symbols_question" + latin:moreKeys="!text/more_keys_for_symbols_question" /> + <Key + latin:keyLabel="!" + latin:moreKeys="!text/more_keys_for_symbols_exclamation" /> </merge> diff --git a/java/res/xml/rowkeys_symbols_shift1.xml b/java/res/xml/rowkeys_symbols_shift1.xml new file mode 100644 index 000000000..6013493e5 --- /dev/null +++ b/java/res/xml/rowkeys_symbols_shift1.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keyLabel="~" /> + <Key + latin:keyLabel="`" /> + <Key + latin:keyLabel="|" /> + <!-- U+2022: "•" BULLET --> + <Key + latin:keyLabel="•" + latin:moreKeys="!text/more_keys_for_bullet" /> + <!-- U+221A: "√" SQUARE ROOT --> + <Key + latin:keyLabel="√" /> + <!-- U+03A0: "Π" GREEK CAPITAL LETTER PI + U+03C0: "π" GREEK SMALL LETTER PI --> + <Key + latin:keyLabel="Π" + latin:moreKeys="π" /> + <!-- U+00F7: "÷" DIVISION SIGN --> + <Key + latin:keyLabel="÷" /> + <!-- U+00D7: "×" MULTIPLICATION SIGN --> + <Key + latin:keyLabel="×" /> + <!-- U+00B6: "¶" PILCROW SIGN + U+00A7: "§" SECTION SIGN --> + <Key + latin:keyLabel="¶" + latin:moreKeys="§" /> + <!-- U+2206: "∆" INCREMENT --> + <Key + latin:keyLabel="∆" /> +</merge> diff --git a/java/res/xml/rowkeys_symbols_shift2.xml b/java/res/xml/rowkeys_symbols_shift2.xml new file mode 100644 index 000000000..36f92143f --- /dev/null +++ b/java/res/xml/rowkeys_symbols_shift2.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keyStyle="moreCurrency1KeyStyle" /> + <Key + latin:keyStyle="moreCurrency2KeyStyle" /> + <Key + latin:keyStyle="moreCurrency3KeyStyle" /> + <Key + latin:keyStyle="moreCurrency4KeyStyle" /> + <!-- U+2191: "↑" UPWARDS ARROW + U+2193: "↓" DOWNWARDS ARROW + U+2190: "←" LEFTWARDS ARROW + U+2192: "→" RIGHTWARDS ARROW --> + <Key + latin:keyLabel="^" + latin:moreKeys="↑,↓,←,→" /> + <!-- U+00B0: "°" DEGREE SIGN + U+2032: "′" PRIME + U+2033: "″" DOUBLE PRIME --> + <Key + latin:keyLabel="°" + latin:moreKeys="′,″" /> + <!-- U+2260: "≠" NOT EQUAL TO + U+2248: "≈" ALMOST EQUAL TO + U+221E: "∞" INFINITY --> + <Key + latin:keyLabel="=" + latin:moreKeys="≠,≈,∞" /> + <include + latin:keyboardLayout="@xml/keys_curly_brackets" /> +</merge> diff --git a/java/res/xml/rowkeys_symbols_shift3.xml b/java/res/xml/rowkeys_symbols_shift3.xml new file mode 100644 index 000000000..5fe1c7450 --- /dev/null +++ b/java/res/xml/rowkeys_symbols_shift3.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keyLabel="\\" /> + <!-- U+00A9: "©" COPYRIGHT SIGN --> + <Key + latin:keyLabel="©" /> + <!-- U+00AE: "®" REGISTERED SIGN --> + <Key + latin:keyLabel="®" /> + <!-- U+2122: "™" TRADE MARK SIGN --> + <Key + latin:keyLabel="™" /> + <!-- U+2105: "℅" CARE OF --> + <Key + latin:keyLabel="℅" /> + <include + latin:keyboardLayout="@xml/keys_square_brackets" /> +</merge> diff --git a/java/res/xml/rows_armenian_phonetic.xml b/java/res/xml/rows_armenian_phonetic.xml index 282dd411b..ea8870e1a 100644 --- a/java/res/xml/rows_armenian_phonetic.xml +++ b/java/res/xml/rows_armenian_phonetic.xml @@ -23,6 +23,8 @@ > <include latin:keyboardLayout="@xml/key_styles_common" /> + <include + latin:keyboardLayout="@xml/key_styles_currency" /> <Row latin:keyWidth="10.0%p" > diff --git a/java/res/xml/rows_nepali_traditional.xml b/java/res/xml/rows_nepali_traditional.xml index fecdc7d5b..7789135ae 100644 --- a/java/res/xml/rows_nepali_traditional.xml +++ b/java/res/xml/rows_nepali_traditional.xml @@ -23,6 +23,8 @@ > <include latin:keyboardLayout="@xml/key_styles_common" /> + <include + latin:keyboardLayout="@xml/key_styles_currency" /> <Row latin:keyWidth="9.091%p" > diff --git a/java/res/xml/rows_symbols.xml b/java/res/xml/rows_symbols.xml index 27010cbd6..3f102e277 100644 --- a/java/res/xml/rows_symbols.xml +++ b/java/res/xml/rows_symbols.xml @@ -23,6 +23,8 @@ > <include latin:keyboardLayout="@xml/key_styles_common" /> + <include + latin:keyboardLayout="@xml/key_styles_currency" /> <Row latin:keyWidth="10%p" > @@ -33,19 +35,30 @@ latin:keyWidth="10%p" > <include - latin:keyboardLayout="@xml/rowkeys_symbols2" /> + latin:keyboardLayout="@xml/rowkeys_symbols2" + latin:keyXPos="5%p" /> </Row> <Row latin:keyWidth="10%p" > + <Key + latin:keyStyle="toMoreSymbolKeyStyle" + latin:keyWidth="15%p" + latin:visualInsetsRight="1%p" /> <include - latin:keyXPos="5%p" latin:keyboardLayout="@xml/rowkeys_symbols3" /> <Key latin:keyStyle="deleteKeyStyle" latin:keyWidth="fillRight" latin:visualInsetsLeft="1%p" /> </Row> - <include - latin:keyboardLayout="@xml/row_symbols4" /> + <Row + latin:keyWidth="10%p" + > + <Key + latin:keyStyle="toAlphaKeyStyle" + latin:keyWidth="15%p" /> + <include + latin:keyboardLayout="@xml/row_symbols4" /> + </Row> </merge> diff --git a/java/res/xml/rows_symbols_shift.xml b/java/res/xml/rows_symbols_shift.xml new file mode 100644 index 000000000..45ada2a61 --- /dev/null +++ b/java/res/xml/rows_symbols_shift.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <include + latin:keyboardLayout="@xml/key_styles_currency" /> + <Row + latin:keyWidth="10%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_symbols_shift1" /> + </Row> + <Row + latin:keyWidth="10%p" + > + <include + latin:keyXPos="5%p" + latin:keyboardLayout="@xml/rowkeys_symbols_shift2" /> + </Row> + <Row + latin:keyWidth="10%p" + > + <Key + latin:keyStyle="backFromMoreSymbolKeyStyle" + latin:keyWidth="15%p" + latin:visualInsetsRight="1%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_symbols_shift3" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" + latin:visualInsetsLeft="1%p" /> + </Row> + <Row + latin:keyWidth="10%p" + > + <Key + latin:keyStyle="toAlphaKeyStyle" + latin:keyWidth="15%p" /> + <include + latin:keyboardLayout="@xml/row_symbols_shift4" /> + </Row> +</merge> diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index b3bb767af..73896dfd3 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -357,6 +357,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp break; case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: + case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: text = context.getText(R.string.spoken_description_shiftmode_on); break; default: @@ -388,6 +389,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp resId = R.string.spoken_description_mode_alpha; break; case KeyboardId.ELEMENT_SYMBOLS: + case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: resId = R.string.spoken_description_mode_symbol; break; case KeyboardId.ELEMENT_PHONE: diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 085ca93d5..58624a2e6 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -156,6 +156,7 @@ public final class KeyCodeDescriptionMapper { resId = R.string.spoken_description_to_symbol; break; case KeyboardId.ELEMENT_SYMBOLS: + case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: resId = R.string.spoken_description_to_alpha; break; case KeyboardId.ELEMENT_PHONE: @@ -190,6 +191,7 @@ public final class KeyCodeDescriptionMapper { break; case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: + case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: resId = R.string.spoken_description_shift_shifted; break; default: diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 53748bb58..736f13ed6 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -50,6 +50,7 @@ public final class KeyboardId { public static final int ELEMENT_ALPHABET_SHIFT_LOCKED = 3; public static final int ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED = 4; public static final int ELEMENT_SYMBOLS = 5; + public static final int ELEMENT_SYMBOLS_SHIFTED = 6; public static final int ELEMENT_PHONE = 7; public static final int ELEMENT_PHONE_SYMBOLS = 8; public static final int ELEMENT_NUMBER = 9; @@ -219,6 +220,7 @@ public final class KeyboardId { case ELEMENT_ALPHABET_SHIFT_LOCKED: return "alphabetShiftLocked"; case ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: return "alphabetShiftLockShifted"; case ELEMENT_SYMBOLS: return "symbols"; + case ELEMENT_SYMBOLS_SHIFTED: return "symbolsShifted"; case ELEMENT_PHONE: return "phone"; case ELEMENT_PHONE_SYMBOLS: return "phoneSymbols"; case ELEMENT_NUMBER: return "number"; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 711de63b3..1eccdf341 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -106,6 +106,8 @@ public final class KeyboardLayoutSet { EditorInfo mEditorInfo; boolean mDisableTouchPositionCorrectionDataForTest; boolean mVoiceKeyEnabled; + // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show + // the voice input key on the symbol layout boolean mVoiceKeyOnMain; boolean mNoSettingsKey; boolean mLanguageSwitchKeyEnabled; @@ -259,6 +261,8 @@ public final class KeyboardLayoutSet { return this; } + // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show + // the voice input key on the symbol layout public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain, final boolean languageSwitchKeyEnabled) { @SuppressWarnings("deprecation") diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index c319c57a1..d128d3595 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -142,7 +142,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); builder.setOptions( settingsValues.isVoiceKeyEnabled(editorInfo), - settingsValues.isVoiceKeyOnMain(), + true /* always show a voice key on the main keyboard */, settingsValues.isLanguageSwitchKeyEnabled()); mKeyboardLayoutSet = builder.build(); try { @@ -271,6 +271,12 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override + public void setSymbolsShiftedKeyboard() { + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); + } + + // Implements {@link KeyboardState.SwitchActions}. + @Override public void requestUpdatingShiftState() { mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState()); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index e4a8f4cb2..f8ad43e74 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -183,6 +183,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; private final KeyTimerHandler mKeyTimerHandler; + private final int mLanguageOnSpacebarHorizontalMargin; private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView> implements TimerProxy { @@ -512,6 +513,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack altCodeKeyWhileTypingFadeinAnimatorResId, this); mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; + + mLanguageOnSpacebarHorizontalMargin = + (int) getResources().getDimension(R.dimen.language_on_spacebar_horizontal_margin); } @Override @@ -1188,26 +1192,27 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } - private static boolean fitsTextIntoWidth(final int width, final String text, - final Paint paint) { + private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) { + final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2; paint.setTextScaleX(1.0f); final float textWidth = TypefaceUtils.getLabelWidth(text, paint); if (textWidth < width) { return true; } - final float scaleX = width / textWidth; + final float scaleX = maxTextWidth / textWidth; if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) { return false; } paint.setTextScaleX(scaleX); - return TypefaceUtils.getLabelWidth(text, paint) < width; + return TypefaceUtils.getLabelWidth(text, paint) < maxTextWidth; } // Layout language name on spacebar. - private static String layoutLanguageOnSpacebar(final Paint paint, + private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype, final int width) { + // Choose appropriate language name to fit into the width. final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype); if (fitsTextIntoWidth(width, fullText, paint)) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 089db12a2..b3491d807 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -45,8 +45,9 @@ public final class KeyboardState { public void setAlphabetAutomaticShiftedKeyboard(); public void setAlphabetShiftLockedKeyboard(); public void setAlphabetShiftLockShiftedKeyboard(); - public void setSymbolsKeyboard(); public void setEmojiKeyboard(); + public void setSymbolsKeyboard(); + public void setSymbolsShiftedKeyboard(); /** * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}. @@ -64,11 +65,13 @@ public final class KeyboardState { private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); // TODO: Merge {@link #mSwitchState}, {@link #mIsAlphabetMode}, {@link #mAlphabetShiftState}, - // {@link #mPrevMainKeyboardWasShiftLocked} into single state variable. + // {@link #mIsSymbolShifted}, {@link #mPrevMainKeyboardWasShiftLocked}, and + // {@link #mPrevSymbolsKeyboardWasShifted} into single state variable. private static final int SWITCH_STATE_ALPHA = 0; private static final int SWITCH_STATE_SYMBOL_BEGIN = 1; private static final int SWITCH_STATE_SYMBOL = 2; private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3; + private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4; private static final int SWITCH_STATE_MOMENTARY_ALPHA_SHIFT = 5; private int mSwitchState = SWITCH_STATE_ALPHA; @@ -77,7 +80,9 @@ public final class KeyboardState { private boolean mIsAlphabetMode; private boolean mIsEmojiMode; private AlphabetShiftState mAlphabetShiftState = new AlphabetShiftState(); + private boolean mIsSymbolShifted; private boolean mPrevMainKeyboardWasShiftLocked; + private boolean mPrevSymbolsKeyboardWasShifted; private int mRecapitalizeMode; // For handling double tap. @@ -102,7 +107,7 @@ public final class KeyboardState { } else if (mIsEmojiMode) { return "EMOJI"; } else { - return "SYMBOLS"; + return "SYMBOLS_" + shiftModeToString(mShiftMode); } } } @@ -119,6 +124,7 @@ public final class KeyboardState { // Reset alphabet shift state. mAlphabetShiftState.setShiftLocked(false); mPrevMainKeyboardWasShiftLocked = false; + mPrevSymbolsKeyboardWasShifted = false; mShiftKeyState.onRelease(); mSymbolKeyState.onRelease(); onRestoreKeyboardState(); @@ -139,6 +145,7 @@ public final class KeyboardState { : (mAlphabetShiftState.isShiftedOrShiftLocked() ? MANUAL_SHIFT : UNSHIFT); } else { state.mIsAlphabetShiftLocked = mPrevMainKeyboardWasShiftLocked; + state.mShiftMode = mIsSymbolShifted ? MANUAL_SHIFT : UNSHIFT; } state.mIsValid = true; if (DEBUG_EVENT) { @@ -156,7 +163,11 @@ public final class KeyboardState { } else if (state.mIsEmojiMode) { setEmojiKeyboard(); } else { - setSymbolsKeyboard(); + if (state.mShiftMode == MANUAL_SHIFT) { + setSymbolsShiftedKeyboard(); + } else { + setSymbolsKeyboard(); + } } if (!state.mIsValid) return; @@ -232,8 +243,14 @@ public final class KeyboardState { } if (mIsAlphabetMode) { mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked(); - setSymbolsKeyboard(); + if (mPrevSymbolsKeyboardWasShifted) { + setSymbolsShiftedKeyboard(); + } else { + setSymbolsKeyboard(); + } + mPrevSymbolsKeyboardWasShifted = false; } else { + mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; setAlphabetKeyboard(); if (mPrevMainKeyboardWasShiftLocked) { setShiftLocked(true); @@ -250,6 +267,7 @@ public final class KeyboardState { } if (mIsAlphabetMode) return; + mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; setAlphabetKeyboard(); if (mPrevMainKeyboardWasShiftLocked) { setShiftLocked(true); @@ -257,6 +275,14 @@ public final class KeyboardState { mPrevMainKeyboardWasShiftLocked = false; } + private void toggleShiftInSymbols() { + if (mIsSymbolShifted) { + setSymbolsKeyboard(); + } else { + setSymbolsShiftedKeyboard(); + } + } + private void setAlphabetKeyboard() { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetKeyboard"); @@ -265,6 +291,7 @@ public final class KeyboardState { mSwitchActions.setAlphabetKeyboard(); mIsAlphabetMode = true; mIsEmojiMode = false; + mIsSymbolShifted = false; mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; mSwitchState = SWITCH_STATE_ALPHA; mSwitchActions.requestUpdatingShiftState(); @@ -276,6 +303,19 @@ public final class KeyboardState { } mSwitchActions.setSymbolsKeyboard(); mIsAlphabetMode = false; + mIsSymbolShifted = false; + // Reset alphabet shift state. + mAlphabetShiftState.setShiftLocked(false); + mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; + } + + private void setSymbolsShiftedKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setSymbolsShiftedKeyboard"); + } + mSwitchActions.setSymbolsShiftedKeyboard(); + mIsAlphabetMode = false; + mIsSymbolShifted = true; // Reset alphabet shift state. mAlphabetShiftState.setShiftLocked(false); mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; @@ -337,7 +377,7 @@ public final class KeyboardState { } else if (code == Constants.CODE_CAPSLOCK) { setShiftLocked(!mAlphabetShiftState.isShiftLocked()); } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { - onReleaseSymbol(); + onReleaseSymbol(withSliding); } } @@ -347,11 +387,16 @@ public final class KeyboardState { mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL; } - private void onReleaseSymbol() { + private void onReleaseSymbol(final boolean withSliding) { if (mSymbolKeyState.isChording()) { // Switch back to the previous keyboard mode if the user chords the mode change key and // another key, then releases the mode change key. toggleAlphabetAndSymbols(); + } else if (!withSliding) { + // If the mode change key is being released without sliding, we should forget the + // previous symbols keyboard shift state and simply switch back to symbols layout + // (never symbols shifted) next time the mode gets changed to symbols layout. + mPrevSymbolsKeyboardWasShifted = false; } mSymbolKeyState.onRelease(); } @@ -417,43 +462,48 @@ public final class KeyboardState { if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { return; } - if (!mIsAlphabetMode) { - // There is no shift key on symbols keyboard. - return; - } - mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapShiftKeyTimeout(); - if (!mIsInDoubleTapShiftKey) { - // This is first tap. - mSwitchActions.startDoubleTapShiftKeyTimer(); - } - if (mIsInDoubleTapShiftKey) { - if (mAlphabetShiftState.isManualShifted() || mIsInAlphabetUnshiftedFromShifted) { - // Shift key has been double tapped while in manual shifted or automatic - // shifted state. - setShiftLocked(true); - } else { - // Shift key has been double tapped while in normal state. This is the second - // tap to disable shift locked state, so just ignore this. + if (mIsAlphabetMode) { + mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapShiftKeyTimeout(); + if (!mIsInDoubleTapShiftKey) { + // This is first tap. + mSwitchActions.startDoubleTapShiftKeyTimer(); } - } else { - if (mAlphabetShiftState.isShiftLocked()) { - // Shift key is pressed while shift locked state, we will treat this state as - // shift lock shifted state and mark as if shift key pressed while normal state. - setShifted(SHIFT_LOCK_SHIFTED); - mShiftKeyState.onPress(); - } else if (mAlphabetShiftState.isAutomaticShifted()) { - // Shift key is pressed while automatic shifted, we have to move to manual shifted. - setShifted(MANUAL_SHIFT); - mShiftKeyState.onPress(); - } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) { - // In manual shifted state, we just record shift key has been pressing while - // shifted state. - mShiftKeyState.onPressOnShifted(); + if (mIsInDoubleTapShiftKey) { + if (mAlphabetShiftState.isManualShifted() || mIsInAlphabetUnshiftedFromShifted) { + // Shift key has been double tapped while in manual shifted or automatic + // shifted state. + setShiftLocked(true); + } else { + // Shift key has been double tapped while in normal state. This is the second + // tap to disable shift locked state, so just ignore this. + } } else { - // In base layout, chording or manual shifted mode is started. - setShifted(MANUAL_SHIFT); - mShiftKeyState.onPress(); + if (mAlphabetShiftState.isShiftLocked()) { + // Shift key is pressed while shift locked state, we will treat this state as + // shift lock shifted state and mark as if shift key pressed while normal + // state. + setShifted(SHIFT_LOCK_SHIFTED); + mShiftKeyState.onPress(); + } else if (mAlphabetShiftState.isAutomaticShifted()) { + // Shift key is pressed while automatic shifted, we have to move to manual + // shifted. + setShifted(MANUAL_SHIFT); + mShiftKeyState.onPress(); + } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) { + // In manual shifted state, we just record shift key has been pressing while + // shifted state. + mShiftKeyState.onPressOnShifted(); + } else { + // In base layout, chording or manual shifted mode is started. + setShifted(MANUAL_SHIFT); + mShiftKeyState.onPress(); + } } + } else { + // In symbol mode, just toggle symbol and symbol more keyboard. + toggleShiftInSymbols(); + mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; + mShiftKeyState.onPress(); } } @@ -508,7 +558,11 @@ public final class KeyboardState { mIsInAlphabetUnshiftedFromShifted = true; } } else { - // There is no shift key on symbols keyboard. + // In symbol mode, switch back to the previous keyboard mode if the user chords the + // shift key and another key, then releases the shift key. + if (mShiftKeyState.isChording()) { + toggleShiftInSymbols(); + } } mShiftKeyState.onRelease(); } @@ -522,6 +576,9 @@ public final class KeyboardState { case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: toggleAlphabetAndSymbols(); break; + case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: + toggleShiftInSymbols(); + break; case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: setAlphabetKeyboard(); break; @@ -549,6 +606,13 @@ public final class KeyboardState { } } break; + case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: + if (code == Constants.CODE_SHIFT) { + // Detected only the shift key has been pressed on symbol layout, and then + // released. + mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; + } + break; case SWITCH_STATE_SYMBOL_BEGIN: if (!isSpaceCharacter(code) && (Constants.isLetterCode(code) || code == Constants.CODE_OUTPUT_TEXT)) { @@ -560,6 +624,7 @@ public final class KeyboardState { // characters followed by a space/enter. if (isSpaceCharacter(code)) { toggleAlphabetAndSymbols(); + mPrevSymbolsKeyboardWasShifted = false; } break; } @@ -587,6 +652,7 @@ public final class KeyboardState { case SWITCH_STATE_SYMBOL_BEGIN: return "SYMBOL-BEGIN"; case SWITCH_STATE_SYMBOL: return "SYMBOL"; case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: return "MOMENTARY-ALPHA-SYMBOL"; + case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: return "MOMENTARY-SYMBOL-MORE"; case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: return "MOMENTARY-ALPHA_SHIFT"; default: return null; } @@ -594,7 +660,8 @@ public final class KeyboardState { @Override public String toString() { - return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString() : "SYMBOLS") + return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString() + : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS")) + " shift=" + mShiftKeyState + " symbol=" + mSymbolKeyState + " switch=" + switchStateToString(mSwitchState) + "]"; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 4d3bdb0ca..7008b0619 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -226,29 +226,31 @@ public final class KeyboardTextsSet { /* 121 */ "shortcut_as_more_key", /* 122 */ "action_next_as_more_key", /* 123 */ "action_previous_as_more_key", - /* 124 */ "label_tab_key", - /* 125 */ "label_to_phone_numeric_key", - /* 126 */ "label_to_phone_symbols_key", - /* 127 */ "label_time_am", - /* 128 */ "label_time_pm", - /* 129 */ "keylabel_for_popular_domain", - /* 130 */ "more_keys_for_popular_domain", - /* 131 */ "more_keys_for_smiley", - /* 132 */ "single_laqm_raqm", - /* 133 */ "single_laqm_raqm_rtl", - /* 134 */ "single_raqm_laqm", - /* 135 */ "double_laqm_raqm", - /* 136 */ "double_laqm_raqm_rtl", - /* 137 */ "double_raqm_laqm", - /* 138 */ "single_lqm_rqm", - /* 139 */ "single_9qm_lqm", - /* 140 */ "single_9qm_rqm", - /* 141 */ "double_lqm_rqm", - /* 142 */ "double_9qm_lqm", - /* 143 */ "double_9qm_rqm", - /* 144 */ "more_keys_for_single_quote", - /* 145 */ "more_keys_for_double_quote", - /* 146 */ "more_keys_for_tablet_double_quote", + /* 124 */ "label_to_more_symbol_key", + /* 125 */ "label_to_more_symbol_for_tablet_key", + /* 126 */ "label_tab_key", + /* 127 */ "label_to_phone_numeric_key", + /* 128 */ "label_to_phone_symbols_key", + /* 129 */ "label_time_am", + /* 130 */ "label_time_pm", + /* 131 */ "keylabel_for_popular_domain", + /* 132 */ "more_keys_for_popular_domain", + /* 133 */ "more_keys_for_smiley", + /* 134 */ "single_laqm_raqm", + /* 135 */ "single_laqm_raqm_rtl", + /* 136 */ "single_raqm_laqm", + /* 137 */ "double_laqm_raqm", + /* 138 */ "double_laqm_raqm_rtl", + /* 139 */ "double_raqm_laqm", + /* 140 */ "single_lqm_rqm", + /* 141 */ "single_9qm_lqm", + /* 142 */ "single_9qm_rqm", + /* 143 */ "double_lqm_rqm", + /* 144 */ "double_9qm_lqm", + /* 145 */ "double_9qm_rqm", + /* 146 */ "more_keys_for_single_quote", + /* 147 */ "more_keys_for_double_quote", + /* 148 */ "more_keys_for_tablet_double_quote", }; private static final String EMPTY = ""; @@ -286,7 +288,8 @@ public final class KeyboardTextsSet { // U+2666: "♦" BLACK DIAMOND SUIT // U+2663: "♣" BLACK CLUB SUIT /* 55 */ "\u266A,\u2665,\u2660,\u2666,\u2663", - /* 56 */ EMPTY, + // U+00B1: "±" PLUS-MINUS SIGN + /* 56 */ "\u00B1", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt /* 57 */ "!fixedColumnOrder!3,<,{,[", @@ -358,8 +361,10 @@ public final class KeyboardTextsSet { /* 103 */ "\u2030", /* 104 */ ",", /* 105~ */ - EMPTY, EMPTY, EMPTY, EMPTY, - /* ~108 */ + EMPTY, EMPTY, EMPTY, + /* ~107 */ + // U+2026: "…" HORIZONTAL ELLIPSIS + /* 108 */ "\u2026", /* 109 */ "\'", /* 110 */ "\"", /* 111 */ "\"", @@ -375,22 +380,26 @@ public final class KeyboardTextsSet { /* 121 */ "!icon/shortcut_key|!code/key_shortcut", /* 122 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", /* 123 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", + // Label for "switch to more symbol" modifier key. Must be short to fit on key! + /* 124 */ "= \\ <", + // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! + /* 125 */ "~ [ {", // Label for "Tab" key. Must be short to fit on key! - /* 124 */ "Tab", + /* 126 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! - /* 125 */ "123", + /* 127 */ "123", // Label for "switch to phone symbols" key. Must be short to fit on key! // U+FF0A: "*" FULLWIDTH ASTERISK // U+FF03: "#" FULLWIDTH NUMBER SIGN - /* 126 */ "\uFF0A\uFF03", + /* 128 */ "\uFF0A\uFF03", // Key label for "ante meridiem" - /* 127 */ "AM", + /* 129 */ "AM", // Key label for "post meridiem" - /* 128 */ "PM", - /* 129 */ ".com", + /* 130 */ "PM", + /* 131 */ ".com", // popular web domains for the locale - most popular, displayed on the keyboard - /* 130 */ "!hasLabels!,.net,.org,.gov,.edu", - /* 131 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + /* 132 */ "!hasLabels!,.net,.org,.gov,.edu", + /* 133 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -412,24 +421,24 @@ public final class KeyboardTextsSet { // The following each quotation mark pair consist of // <opening quotation mark>, <closing quotation mark> // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>. - /* 132 */ "\u2039,\u203A", - /* 133 */ "\u2039|\u203A,\u203A|\u2039", - /* 134 */ "\u203A,\u2039", - /* 135 */ "\u00AB,\u00BB", - /* 136 */ "\u00AB|\u00BB,\u00BB|\u00AB", - /* 137 */ "\u00BB,\u00AB", + /* 134 */ "\u2039,\u203A", + /* 135 */ "\u2039|\u203A,\u203A|\u2039", + /* 136 */ "\u203A,\u2039", + /* 137 */ "\u00AB,\u00BB", + /* 138 */ "\u00AB|\u00BB,\u00BB|\u00AB", + /* 139 */ "\u00BB,\u00AB", // The following each quotation mark triplet consists of // <another quotation mark>, <opening quotation mark>, <closing quotation mark> // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>. - /* 138 */ "\u201A,\u2018,\u2019", - /* 139 */ "\u2019,\u201A,\u2018", - /* 140 */ "\u2018,\u201A,\u2019", - /* 141 */ "\u201E,\u201C,\u201D", - /* 142 */ "\u201D,\u201E,\u201C", - /* 143 */ "\u201C,\u201E,\u201D", - /* 144 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", - /* 145 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", - /* 146 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", + /* 140 */ "\u201A,\u2018,\u2019", + /* 141 */ "\u2019,\u201A,\u2018", + /* 142 */ "\u2018,\u201A,\u2019", + /* 143 */ "\u201E,\u201C,\u201D", + /* 144 */ "\u201D,\u201E,\u201C", + /* 145 */ "\u201C,\u201E,\u201D", + /* 146 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", + /* 147 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", + /* 148 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", }; /* Language af: Afrikaans */ @@ -1916,8 +1925,9 @@ public final class KeyboardTextsSet { // U+2605: "★" BLACK STAR /* 54 */ "\u2605", /* 55 */ null, + // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN - /* 56 */ "\uFB29", + /* 56 */ "\u00B1,\uFB29", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt /* 57 */ "!fixedColumnOrder!3,<|>,{|},[|]", diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index b92283c5b..c884e7b1f 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -24,13 +24,14 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter; +import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor; import java.io.File; import java.util.ArrayList; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Abstract base class for an expandable dictionary that can be created and updated dynamically @@ -48,19 +49,27 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** Whether to print debug output to log */ private static boolean DEBUG = false; + // TODO: Remove and enable dynamic update in native code. + /** Whether to call binary dictionary dynamically updating methods. */ + private static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = false; + + private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; + /** * The maximum length of a word in this dictionary. */ protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; /** - * A static map of locks, each of which controls access to a single binary dictionary file. They - * ensure that only one instance can update the same dictionary at the same time. The key for - * this map is the filename and the value is the shared dictionary controller associated with - * that filename. + * A static map of time recorders, each of which records the time of accesses to a single binary + * dictionary file. The key for this map is the filename and the value is the shared dictionary + * time recorder associated with that filename. */ - private static final HashMap<String, DictionaryController> sSharedDictionaryControllers = - CollectionUtils.newHashMap(); + private static volatile ConcurrentHashMap<String, DictionaryTimeRecorder> + sFilenameDictionaryTimeRecorderMap = CollectionUtils.newConcurrentHashMap(); + + private static volatile ConcurrentHashMap<String, PrioritizedSerialExecutor> + sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap(); /** The application context. */ protected final Context mContext; @@ -71,30 +80,34 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private BinaryDictionary mBinaryDictionary; + // TODO: Remove and handle dictionaries in native code. /** The in-memory dictionary used to generate the binary dictionary. */ - private AbstractDictionaryWriter mDictionaryWriter; + protected AbstractDictionaryWriter mDictionaryWriter; /** * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple * dictionary instances with the same filename is supported, with access controlled by - * DictionaryController. + * DictionaryTimeRecorder. */ private final String mFilename; /** Whether to support dynamically updating the dictionary */ private final boolean mIsUpdatable; - /** Controls access to the shared binary dictionary file across multiple instances. */ - private final DictionaryController mSharedDictionaryController; + // TODO: remove, once dynamic operations is serialized + /** Records access to the shared binary dictionary file across multiple instances. */ + private final DictionaryTimeRecorder mFilenameDictionaryTimeRecorder; - /** Controls access to the local binary dictionary for this instance. */ - private final DictionaryController mLocalDictionaryController = new DictionaryController(); + // TODO: remove, once dynamic operations is serialized + /** Records access to the local binary dictionary for this instance. */ + private final DictionaryTimeRecorder mPerInstanceDictionaryTimeRecorder = + new DictionaryTimeRecorder(); /* A extension for a binary dictionary file. */ public static final String DICT_FILE_EXTENSION = ".dict"; - private final AtomicReference<AsyncWriteBinaryDictionaryTask> mWaitingTask = - new AtomicReference<AsyncWriteBinaryDictionaryTask>(); + private final AtomicReference<Runnable> mUnfinishedFlushingTask = + new AtomicReference<Runnable>(); /** * Abstract method for loading the unigrams and bigrams of a given dictionary in a background @@ -110,16 +123,32 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected abstract boolean hasContentChanged(); /** - * Gets the shared dictionary controller for the given filename. + * Gets the dictionary time recorder for the given filename. */ - private static synchronized DictionaryController getSharedDictionaryController( + private static DictionaryTimeRecorder getDictionaryTimeRecorder( String filename) { - DictionaryController controller = sSharedDictionaryControllers.get(filename); - if (controller == null) { - controller = new DictionaryController(); - sSharedDictionaryControllers.put(filename, controller); + DictionaryTimeRecorder recorder = sFilenameDictionaryTimeRecorderMap.get(filename); + if (recorder == null) { + synchronized(sFilenameDictionaryTimeRecorderMap) { + recorder = new DictionaryTimeRecorder(); + sFilenameDictionaryTimeRecorderMap.put(filename, recorder); + } + } + return recorder; + } + + /** + * Gets the executor for the given filename. + */ + private static PrioritizedSerialExecutor getExecutor(final String filename) { + PrioritizedSerialExecutor executor = sFilenameExecutorMap.get(filename); + if (executor == null) { + synchronized(sFilenameExecutorMap) { + executor = new PrioritizedSerialExecutor(); + sFilenameExecutorMap.put(filename, executor); + } } - return controller; + return executor; } private static AbstractDictionaryWriter getDictionaryWriter(final Context context, @@ -148,7 +177,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mContext = context; mIsUpdatable = isUpdatable; mBinaryDictionary = null; - mSharedDictionaryController = getSharedDictionaryController(filename); + mFilenameDictionaryTimeRecorder = getDictionaryTimeRecorder(filename); // Currently, only dynamic personalization dictionary is updatable. mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable); } @@ -162,35 +191,38 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ @Override public void close() { - closeBinaryDictionary(); - mLocalDictionaryController.writeLock().lock(); - try { - mDictionaryWriter.close(); - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + if (mBinaryDictionary!= null) { + mBinaryDictionary.close(); + mBinaryDictionary = null; + } + mDictionaryWriter.close(); + } + }); } protected void closeBinaryDictionary() { // Ensure that no other threads are accessing the local binary dictionary. - mLocalDictionaryController.writeLock().lock(); - try { - if (mBinaryDictionary != null) { - mBinaryDictionary.close(); - mBinaryDictionary = null; + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + if (mBinaryDictionary != null) { + mBinaryDictionary.close(); + mBinaryDictionary = null; + } } - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + }); } protected void clear() { - mLocalDictionaryController.writeLock().lock(); - try { - mDictionaryWriter.clear(); - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + mDictionaryWriter.clear(); + } + }); } /** @@ -219,14 +251,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename); return; } - // TODO: Use a queue to reflect what needs to be reflected. - if (mLocalDictionaryController.writeLock().tryLock()) { - try { + + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + mBinaryDictionary.addUnigramWord(word, frequency); + } + // TODO: Remove. mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); - } finally { - mLocalDictionaryController.writeLock().unlock(); } - } + }); } /** @@ -239,15 +274,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { + mFilename); return; } - // TODO: Use a queue to reflect what needs to be reflected. - if (mLocalDictionaryController.writeLock().tryLock()) { - try { + + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + mBinaryDictionary.addBigramWords(word0, word1, frequency); + } + // TODO: Remove. mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid, 0 /* lastTouchedTime */); - } finally { - mLocalDictionaryController.writeLock().unlock(); } - } + }); } /** @@ -259,24 +297,30 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { + mFilename); return; } - // TODO: Use a queue to reflect what needs to be reflected. - if (mLocalDictionaryController.writeLock().tryLock()) { - try { + + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + mBinaryDictionary.removeBigramWords(word0, word1); + } + // TODO: Remove. mDictionaryWriter.removeBigramWords(word0, word1); - } finally { - mLocalDictionaryController.writeLock().unlock(); } - } + }); } @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { - asyncReloadDictionaryIfRequired(); - // Write lock because getSuggestions in native updates session status. - if (mLocalDictionaryController.writeLock().tryLock()) { - try { + reloadDictionaryIfRequired(); + final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); + final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder = + new AsyncResultHolder<ArrayList<SuggestedWordInfo>>(); + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { final ArrayList<SuggestedWordInfo> inMemDictSuggestion = mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions); @@ -286,38 +330,37 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions); if (inMemDictSuggestion == null) { - return binarySuggestion; + holder.set(binarySuggestion); } else if (binarySuggestion == null) { - return inMemDictSuggestion; + holder.set(inMemDictSuggestion); } else { binarySuggestion.addAll(inMemDictSuggestion); - return binarySuggestion; + holder.set(binarySuggestion); } } else { - return inMemDictSuggestion; + holder.set(inMemDictSuggestion); } - } finally { - mLocalDictionaryController.writeLock().unlock(); } - } - return null; + }); + + return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); } @Override public boolean isValidWord(final String word) { - asyncReloadDictionaryIfRequired(); + reloadDictionaryIfRequired(); return isValidWordInner(word); } protected boolean isValidWordInner(final String word) { - if (mLocalDictionaryController.readLock().tryLock()) { - try { - return isValidWordLocked(word); - } finally { - mLocalDictionaryController.readLock().unlock(); + final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { + holder.set(isValidWordLocked(word)); } - } - return false; + }); + return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); } protected boolean isValidWordLocked(final String word) { @@ -335,8 +378,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * dictionary exists, this method will generate one. */ protected void loadDictionary() { - mLocalDictionaryController.mLastUpdateRequestTime = SystemClock.uptimeMillis(); - asyncReloadDictionaryIfRequired(); + mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = SystemClock.uptimeMillis(); + reloadDictionaryIfRequired(); } /** @@ -346,8 +389,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private void loadBinaryDictionary() { if (DEBUG) { Log.d(TAG, "Loading binary dictionary: " + mFilename + " request=" - + mSharedDictionaryController.mLastUpdateRequestTime + " update=" - + mSharedDictionaryController.mLastUpdateTime); + + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update=" + + mFilenameDictionaryTimeRecorder.mLastUpdateTime); } final File file = new File(mContext.getFilesDir(), mFilename); @@ -358,20 +401,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length, true /* useFullEditDistance */, null, mDictType, mIsUpdatable); - if (mBinaryDictionary != null) { - // Ensure all threads accessing the current dictionary have finished before swapping in - // the new one. - final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; - mLocalDictionaryController.writeLock().lock(); - try { + // Ensure all threads accessing the current dictionary have finished before swapping in + // the new one. + final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { mBinaryDictionary = newBinaryDictionary; - } finally { - mLocalDictionaryController.writeLock().unlock(); + if (oldBinaryDictionary != null) { + oldBinaryDictionary.close(); + } } - oldBinaryDictionary.close(); - } else { - mBinaryDictionary = newBinaryDictionary; - } + }); } /** @@ -386,8 +427,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private void writeBinaryDictionary() { if (DEBUG) { Log.d(TAG, "Generating binary dictionary: " + mFilename + " request=" - + mSharedDictionaryController.mLastUpdateRequestTime + " update=" - + mSharedDictionaryController.mLastUpdateTime); + + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update=" + + mFilenameDictionaryTimeRecorder.mLastUpdateTime); } if (needsToReloadBeforeWriting()) { mDictionaryWriter.clear(); @@ -405,54 +446,42 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected void setRequiresReload(final boolean requiresRebuild) { final long time = SystemClock.uptimeMillis(); - mLocalDictionaryController.mLastUpdateRequestTime = time; - mSharedDictionaryController.mLastUpdateRequestTime = time; + mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = time; + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime = time; if (DEBUG) { Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update=" - + mSharedDictionaryController.mLastUpdateTime); - } - } - - /** - * Reloads the dictionary if required. Reload will occur asynchronously in a separate thread. - */ - public void asyncReloadDictionaryIfRequired() { - if (!isReloadRequired()) return; - if (DEBUG) { - Log.d(TAG, "Starting AsyncReloadDictionaryTask: " + mFilename); + + mFilenameDictionaryTimeRecorder.mLastUpdateTime); } - new AsyncReloadDictionaryTask().start(); } /** * Reloads the dictionary if required. */ - public final void syncReloadDictionaryIfRequired() { + public final void reloadDictionaryIfRequired() { if (!isReloadRequired()) return; - syncReloadDictionaryInternal(); + reloadDictionary(); } /** * Returns whether a dictionary reload is required. */ private boolean isReloadRequired() { - return mBinaryDictionary == null || mLocalDictionaryController.isOutOfDate(); + return mBinaryDictionary == null || mPerInstanceDictionaryTimeRecorder.isOutOfDate(); } /** * Reloads the dictionary. Access is controlled on a per dictionary file basis and supports * concurrent calls from multiple instances that share the same dictionary file. */ - private final void syncReloadDictionaryInternal() { + private final void reloadDictionary() { // Ensure that only one thread attempts to read or write to the shared binary dictionary // file at the same time. - mLocalDictionaryController.writeLock().lock(); - try { - mSharedDictionaryController.writeLock().lock(); - try { + getExecutor(mFilename).execute(new Runnable() { + @Override + public void run() { final long time = SystemClock.uptimeMillis(); final boolean dictionaryFileExists = dictionaryFileExists(); - if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) { + if (mFilenameDictionaryTimeRecorder.isOutOfDate() || !dictionaryFileExists) { // If the shared dictionary file does not exist or is out of date, the first // instance that acquires the lock will generate a new one. if (hasContentChanged() || !dictionaryFileExists) { @@ -460,34 +489,31 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // rebuild the binary dictionary. Empty dictionaries are supported (in the // case where loadDictionaryAsync() adds nothing) in order to provide a // uniform framework. - mSharedDictionaryController.mLastUpdateTime = time; + mFilenameDictionaryTimeRecorder.mLastUpdateTime = time; writeBinaryDictionary(); loadBinaryDictionary(); } else { // If not, the reload request was unnecessary so revert // LastUpdateRequestTime to LastUpdateTime. - mSharedDictionaryController.mLastUpdateRequestTime = - mSharedDictionaryController.mLastUpdateTime; + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime = + mFilenameDictionaryTimeRecorder.mLastUpdateTime; } - } else if (mBinaryDictionary == null || mLocalDictionaryController.mLastUpdateTime - < mSharedDictionaryController.mLastUpdateTime) { + } else if (mBinaryDictionary == null || + mPerInstanceDictionaryTimeRecorder.mLastUpdateTime + < mFilenameDictionaryTimeRecorder.mLastUpdateTime) { // Otherwise, if the local dictionary is older than the shared dictionary, load // the shared dictionary. loadBinaryDictionary(); } if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) { // Binary dictionary is not valid. Regenerate the dictionary file. - mSharedDictionaryController.mLastUpdateTime = time; + mFilenameDictionaryTimeRecorder.mLastUpdateTime = time; writeBinaryDictionary(); loadBinaryDictionary(); } - mLocalDictionaryController.mLastUpdateTime = time; - } finally { - mSharedDictionaryController.writeLock().unlock(); + mPerInstanceDictionaryTimeRecorder.mLastUpdateTime = time; } - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + }); } // TODO: cache the file's existence so that we avoid doing a disk access each time. @@ -497,83 +523,36 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } /** - * Thread class for asynchronously reloading and rewriting the binary dictionary. - */ - private class AsyncReloadDictionaryTask extends Thread { - @Override - public void run() { - syncReloadDictionaryInternal(); - } - } - - /** * Load the dictionary to memory. */ protected void asyncLoadDictionaryToMemory() { - new AsyncLoadDictionaryToMemoryTask().start(); - } - - /** - * Thread class for asynchronously loading dictionary to memory. - */ - private class AsyncLoadDictionaryToMemoryTask extends Thread { - @Override - public void run() { - mLocalDictionaryController.writeLock().lock(); - try { - mSharedDictionaryController.readLock().lock(); - try { - loadDictionaryAsync(); - } finally { - mSharedDictionaryController.readLock().unlock(); - } - } finally { - mLocalDictionaryController.writeLock().unlock(); + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { + loadDictionaryAsync(); } - } + }); } /** * Generate binary dictionary using DictionaryWriter. */ protected void asyncWriteBinaryDictionary() { - final AsyncWriteBinaryDictionaryTask newTask = new AsyncWriteBinaryDictionaryTask(); - newTask.start(); - final AsyncWriteBinaryDictionaryTask oldTask = mWaitingTask.getAndSet(newTask); - if (oldTask != null) { - oldTask.interrupt(); - } - } - - /** - * Thread class for asynchronously writing the binary dictionary. - */ - private class AsyncWriteBinaryDictionaryTask extends Thread { - @Override - public void run() { - mSharedDictionaryController.writeLock().lock(); - try { - mLocalDictionaryController.writeLock().lock(); - try { - if (isInterrupted()) { - return; - } - writeBinaryDictionary(); - } finally { - mLocalDictionaryController.writeLock().unlock(); - } - } finally { - mSharedDictionaryController.writeLock().unlock(); + final Runnable newTask = new Runnable() { + @Override + public void run() { + writeBinaryDictionary(); } - } + }; + final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask); + getExecutor(mFilename).replaceAndExecute(oldTask, newTask); } /** - * Lock for controlling access to a given binary dictionary and for tracking whether the - * dictionary is out of date. Can be shared across multiple dictionary instances that access the - * same filename. + * Time recorder for tracking whether the dictionary is out of date. + * Can be shared across multiple dictionary instances that access the same filename. */ - private static class DictionaryController extends ReentrantReadWriteLock { + private static class DictionaryTimeRecorder { private volatile long mLastUpdateTime = 0; private volatile long mLastUpdateRequestTime = 0; @@ -588,12 +567,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @UsedForTesting protected void addWordDynamicallyForTests(final String word, final String shortcutTarget, final int frequency, final boolean isNotAWord) { - mLocalDictionaryController.writeLock().lock(); - try { - addWordDynamically(word, shortcutTarget, frequency, isNotAWord); - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { + addWordDynamically(word, shortcutTarget, frequency, isNotAWord); + } + }); } /** @@ -602,12 +581,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @UsedForTesting protected void addBigramDynamicallyForTests(final String word0, final String word1, final int frequency, final boolean isValid) { - mLocalDictionaryController.writeLock().lock(); - try { - addBigramDynamically(word0, word1, frequency, isValid); - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { + addBigramDynamically(word0, word1, frequency, isValid); + } + }); } /** @@ -615,11 +594,27 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ @UsedForTesting protected void removeBigramDynamicallyForTests(final String word0, final String word1) { - mLocalDictionaryController.writeLock().lock(); - try { - removeBigramDynamically(word0, word1); - } finally { - mLocalDictionaryController.writeLock().unlock(); - } + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { + removeBigramDynamically(word0, word1); + } + }); + } + + // TODO: Implement native binary methods once the dynamic dictionary implementation is done. + @UsedForTesting + public boolean isInDictionaryForTests(final String word) { + final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); + getExecutor(mFilename).executePrioritized(new Runnable() { + @Override + public void run() { + if (mDictType == Dictionary.TYPE_USER_HISTORY) { + holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter) + .isInDictionaryForTests(word)); + } + } + }); + return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); } } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 342dcfc5e..ba7d1a2b0 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -265,10 +265,10 @@ public class ExpandableDictionary extends Dictionary { return (node == null) ? false : !node.mShortcutOnly; } - public boolean removeBigram(final String word1, final String word2) { + public boolean removeBigram(final String word0, final String word1) { // Refer to addOrSetBigram() about word1.toLowerCase() - final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null); - final Node secondWord = searchWord(mRoots, word2, 0, null); + final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null); + final Node secondWord = searchWord(mRoots, word1, 0, null); LinkedList<NextWord> bigrams = firstWord.mNGrams; NextWord bigramNode = null; if (bigrams == null || bigrams.size() == 0) { @@ -297,10 +297,10 @@ public class ExpandableDictionary extends Dictionary { return (node == null) ? -1 : node.mFrequency; } - public NextWord getBigramWord(final String word1, final String word2) { - // Refer to addOrSetBigram() about word1.toLowerCase() - final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null); - final Node secondWord = searchWord(mRoots, word2, 0, null); + public NextWord getBigramWord(final String word0, final String word1) { + // Refer to addOrSetBigram() about word0.toLowerCase() + final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null); + final Node secondWord = searchWord(mRoots, word1, 0, null); LinkedList<NextWord> bigrams = firstWord.mNGrams; if (bigrams == null || bigrams.size() == 0) { return null; @@ -473,37 +473,41 @@ public class ExpandableDictionary extends Dictionary { } } - public int setBigramAndGetFrequency(final String word1, final String word2, + public int setBigramAndGetFrequency(final String word0, final String word1, final int frequency) { - return setBigramAndGetFrequency(word1, word2, frequency, null /* unused */); + return setBigramAndGetFrequency(word0, word1, frequency, null /* unused */); } - public int setBigramAndGetFrequency(final String word1, final String word2, + public int setBigramAndGetFrequency(final String word0, final String word1, final ForgettingCurveParams fcp) { - return setBigramAndGetFrequency(word1, word2, 0 /* unused */, fcp); + return setBigramAndGetFrequency(word0, word1, 0 /* unused */, fcp); } /** * Adds bigrams to the in-memory trie structure that is being used to retrieve any word - * @param word1 the first word of this bigram - * @param word2 the second word of this bigram + * @param word0 the first word of this bigram + * @param word1 the second word of this bigram * @param frequency frequency for this bigram * @param fcp an instance of ForgettingCurveParams to use for decay policy * @return returns the final bigram frequency */ - private int setBigramAndGetFrequency(final String word1, final String word2, + private int setBigramAndGetFrequency(final String word0, final String word1, final int frequency, final ForgettingCurveParams fcp) { + if (TextUtils.isEmpty(word0)) { + Log.e(TAG, "Invalid bigram previous word: " + word0); + return frequency; + } // We don't want results to be different according to case of the looked up left hand side // word. We do want however to return the correct case for the right hand side. // So we want to squash the case of the left hand side, and preserve that of the right // hand side word. - final String word1Lower = word1.toLowerCase(); - if (TextUtils.isEmpty(word1Lower) || TextUtils.isEmpty(word2)) { - Log.e(TAG, "Invalid bigram pair: " + word1 + ", " + word1Lower + ", " + word2); + final String word0Lower = word0.toLowerCase(); + if (TextUtils.isEmpty(word0Lower) || TextUtils.isEmpty(word1)) { + Log.e(TAG, "Invalid bigram pair: " + word0 + ", " + word0Lower + ", " + word1); return frequency; } - final Node firstWord = searchWord(mRoots, word1Lower, 0, null); - final Node secondWord = searchWord(mRoots, word2, 0, null); + final Node firstWord = searchWord(mRoots, word0Lower, 0, null); + final Node secondWord = searchWord(mRoots, word1, 0, null); LinkedList<NextWord> bigrams = firstWord.mNGrams; if (bigrams == null || bigrams.size() == 0) { firstWord.mNGrams = CollectionUtils.newLinkedList(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 6be9ded5c..6c83ac7ed 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -86,6 +86,7 @@ import com.android.inputmethod.latin.settings.SettingsActivity; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.utils.ApplicationUtils; +import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -107,8 +108,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Locale; import java.util.TreeSet; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * Input method implementation for Qwerty'ish keyboard. @@ -2218,7 +2217,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mKeyboardSwitcher.updateShiftState(); } - // Returns true if we did an autocorrection, false otherwise. + // Returns true if we do an autocorrection, false otherwise. private boolean handleSeparator(final int primaryCode, final int x, final int y, final int spaceState) { boolean didAutoCorrect = false; @@ -2385,31 +2384,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - final CountDownLatch latch = new CountDownLatch(1); - final SuggestedWords[] suggestedWordsArray = new SuggestedWords[1]; + final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>(); getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - suggestedWordsArray[0] = suggestedWords; - latch.countDown(); + holder.set(suggestedWords); } } ); - // TODO: Quit blocking the main thread. - try { - // Wait for the result of getSuggestedWords - // We set the time out to avoid ANR. - latch.await(GET_SUGGESTED_WORDS_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // TODO: Cancel all pending "getSuggestedWords" tasks when it failed. We may want to add - // "onGetSuggestionFailed" to "OnGetSuggestedWordsCallback". - Log.e(TAG, "InterruptedException while waiting for getSuggestedWords.", e); - return; - } - if (suggestedWordsArray[0] != null) { - showSuggestionStrip(suggestedWordsArray[0]); + // This line may cause the current thread to wait. + final SuggestedWords suggestedWords = holder.get(null, GET_SUGGESTED_WORDS_TIMEOUT); + if (suggestedWords != null) { + showSuggestionStrip(suggestedWords); } } @@ -2489,11 +2477,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen false /* isPrediction */); } - private void showSuggestionStrip(final SuggestedWords suggestedWords) { - if (suggestedWords.isEmpty()) { - clearSuggestionStrip(); - return; - } + private void setAutoCorrection(final SuggestedWords suggestedWords) { + if (suggestedWords.isEmpty()) return; final String autoCorrection; if (suggestedWords.mWillAutoCorrect) { autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); @@ -2501,13 +2486,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD); } mWordComposer.setAutoCorrection(autoCorrection); + } + + private void showSuggestionStrip(final SuggestedWords suggestedWords) { + if (suggestedWords.isEmpty()) { + clearSuggestionStrip(); + return; + } + setAutoCorrection(suggestedWords); final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); setSuggestedWords(suggestedWords, isAutoCorrection); setAutoCorrectionIndicator(isAutoCorrection); setSuggestionStripShown(isSuggestionsStripVisible()); } - private void commitCurrentAutoCorrection(final String separatorString) { + private void commitCurrentAutoCorrection(final String separator) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { updateSuggestionStrip(); @@ -2523,16 +2516,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if (mSettings.isInternal()) { LatinImeLoggerUtils.onAutoCorrection( - typedWord, autoCorrection, separatorString, mWordComposer); + typedWord, autoCorrection, separator, mWordComposer); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { final SuggestedWords suggestedWords = mSuggestedWords; ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection, - separatorString, mWordComposer.isBatchMode(), suggestedWords); + separator, mWordComposer.isBatchMode(), suggestedWords); } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, - separatorString); + separator); if (!typedWord.equals(autoCorrection)) { // This will make the correction flash for a short while as a visual clue // to the user that auto-correction happened. It has no other effect; in particular diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java index 67ef538ac..3213c92c7 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java @@ -35,14 +35,14 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, final String prevWordForBigrams, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { - syncReloadDictionaryIfRequired(); + reloadDictionaryIfRequired(); return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords, additionalFeaturesOptions); } @Override public synchronized boolean isValidWord(final String word) { - syncReloadDictionaryIfRequired(); + reloadDictionaryIfRequired(); return isValidWordInner(word); } diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java index bea522320..6405b5e46 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java @@ -38,14 +38,14 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, final String prevWordForBigrams, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { - syncReloadDictionaryIfRequired(); + reloadDictionaryIfRequired(); return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords, additionalFeaturesOptions); } @Override public synchronized boolean isValidWord(final String word) { - syncReloadDictionaryIfRequired(); + reloadDictionaryIfRequired(); return isValidWordInner(word); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index 79f5ad8bd..5a213415a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -27,7 +27,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Iterator; /** * Encodes binary files for a FusionDictionary. @@ -432,7 +431,7 @@ public class BinaryDictEncoderUtils { * @param formatOptions file format options. * @return the same array it was passed. The nodes have been updated for address and size. */ - private static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict, + /* package */ static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict, final ArrayList<PtNodeArray> flatNodes, final FormatOptions formatOptions) { // First get the worst possible sizes and offsets for (final PtNodeArray n : flatNodes) calculatePtNodeArrayMaximumSize(n, formatOptions); @@ -484,7 +483,7 @@ public class BinaryDictEncoderUtils { * * @param arrays the list of node arrays to check */ - private static void checkFlatPtNodeArrayList(final ArrayList<PtNodeArray> arrays) { + /* package */ static void checkFlatPtNodeArrayList(final ArrayList<PtNodeArray> arrays) { int offset = 0; int index = 0; for (final PtNodeArray ptNodeArray : arrays) { @@ -501,52 +500,53 @@ public class BinaryDictEncoderUtils { } /** - * Helper method to write a variable-size address to a file. + * Helper method to write a children position to a file. * * @param buffer the buffer to write to. * @param index the index in the buffer to write the address to. - * @param address the address to write. + * @param position the position to write. * @return the size in bytes the address actually took. */ - private static int writeVariableAddress(final byte[] buffer, int index, final int address) { - switch (getByteSize(address)) { + /* package */ static int writeChildrenPosition(final byte[] buffer, int index, + final int position) { + switch (getByteSize(position)) { case 1: - buffer[index++] = (byte)address; + buffer[index++] = (byte)position; return 1; case 2: - buffer[index++] = (byte)(0xFF & (address >> 8)); - buffer[index++] = (byte)(0xFF & address); + buffer[index++] = (byte)(0xFF & (position >> 8)); + buffer[index++] = (byte)(0xFF & position); return 2; case 3: - buffer[index++] = (byte)(0xFF & (address >> 16)); - buffer[index++] = (byte)(0xFF & (address >> 8)); - buffer[index++] = (byte)(0xFF & address); + buffer[index++] = (byte)(0xFF & (position >> 16)); + buffer[index++] = (byte)(0xFF & (position >> 8)); + buffer[index++] = (byte)(0xFF & position); return 3; case 0: return 0; default: - throw new RuntimeException("Address " + address + " has a strange size"); + throw new RuntimeException("Position " + position + " has a strange size"); } } /** - * Helper method to write a variable-size signed address to a file. + * Helper method to write a signed children position to a file. * * @param buffer the buffer to write to. * @param index the index in the buffer to write the address to. - * @param address the address to write. + * @param position the position to write. * @return the size in bytes the address actually took. */ - private static int writeVariableSignedAddress(final byte[] buffer, int index, - final int address) { - if (!BinaryDictIOUtils.hasChildrenAddress(address)) { + /* package */ static int writeSignedChildrenPosition(final byte[] buffer, int index, + final int position) { + if (!BinaryDictIOUtils.hasChildrenAddress(position)) { buffer[index] = buffer[index + 1] = buffer[index + 2] = 0; } else { - final int absAddress = Math.abs(address); + final int absPosition = Math.abs(position); buffer[index++] = - (byte)((address < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absAddress >> 16))); - buffer[index++] = (byte)(0xFF & (absAddress >> 8)); - buffer[index++] = (byte)(0xFF & absAddress); + (byte)((position < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absPosition >> 16))); + buffer[index++] = (byte)(0xFF & (absPosition >> 8)); + buffer[index++] = (byte)(0xFF & absPosition); } return 3; } @@ -598,7 +598,7 @@ public class BinaryDictEncoderUtils { return flags; } - private static byte makePtNodeFlags(final PtNode node, final int ptNodeAddress, + /* package */ static byte makePtNodeFlags(final PtNode node, final int ptNodeAddress, final int childrenOffset, final FormatOptions formatOptions) { return (byte) makePtNodeFlags(node.mChars.length > 1, node.mFrequency >= 0, getByteSize(childrenOffset), @@ -616,7 +616,7 @@ public class BinaryDictEncoderUtils { * @param word the second bigram, for debugging purposes * @return the flags */ - private static final int makeBigramFlags(final boolean more, final int offset, + /* package */ static final int makeBigramFlags(final boolean more, final int offset, int bigramFrequency, final int unigramFrequency, final String word) { int bigramFlags = (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0) + (offset < 0 ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0); @@ -702,7 +702,7 @@ public class BinaryDictEncoderUtils { + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY); } - private static final int writeParentAddress(final byte[] buffer, final int index, + /* package */ static final int writeParentAddress(final byte[] buffer, final int index, final int address, final FormatOptions formatOptions) { if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { if (address == FormatSpec.NO_PARENT_ADDRESS) { @@ -721,137 +721,69 @@ public class BinaryDictEncoderUtils { } } + /* package */ static final int getChildrenPosition(final PtNode ptNode, + final FormatOptions formatOptions) { + int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate + + getNodeHeaderSize(ptNode, formatOptions); + if (ptNode.mFrequency >= 0) { + positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE; + } + return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS + : ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField; + } + /** * Write a PtNodeArray to memory. The PtNodeArray is expected to have its final position cached. * * @param dict the dictionary the node array is a part of (for relative offsets). - * @param buffer the memory buffer to write to. + * @param dictEncoder the dictionary encoder. * @param ptNodeArray the node array to write. * @param formatOptions file format options. - * @return the address of the END of the node. */ @SuppressWarnings("unused") - private static int writePlacedNode(final FusionDictionary dict, byte[] buffer, - final PtNodeArray ptNodeArray, final FormatOptions formatOptions) { + /* package */ static void writePlacedNode(final FusionDictionary dict, + final DictEncoder dictEncoder, final PtNodeArray ptNodeArray, + final FormatOptions formatOptions) { // TODO: Make the code in common with BinaryDictIOUtils#writePtNode - int index = ptNodeArray.mCachedAddressAfterUpdate; + dictEncoder.setPosition(ptNodeArray.mCachedAddressAfterUpdate); final int ptNodeCount = ptNodeArray.mData.size(); - final int countSize = getPtNodeCountSize(ptNodeArray); - final int parentAddress = ptNodeArray.mCachedParentAddress; - if (1 == countSize) { - buffer[index++] = (byte)ptNodeCount; - } else if (2 == countSize) { - // We need to signal 2-byte size by setting the top bit of the MSB to 1, so - // we | 0x80 to do this. - buffer[index++] = (byte)((ptNodeCount >> 8) | 0x80); - buffer[index++] = (byte)(ptNodeCount & 0xFF); - } else { - throw new RuntimeException("Strange size from getGroupCountSize : " + countSize); - } - int ptNodeAddress = index; + dictEncoder.writePtNodeCount(ptNodeCount); + final int parentPosition = + (ptNodeArray.mCachedParentAddress == FormatSpec.NO_PARENT_ADDRESS) + ? FormatSpec.NO_PARENT_ADDRESS + : ptNodeArray.mCachedParentAddress + ptNodeArray.mCachedAddressAfterUpdate; for (int i = 0; i < ptNodeCount; ++i) { final PtNode ptNode = ptNodeArray.mData.get(i); - if (index != ptNode.mCachedAddressAfterUpdate) { + if (dictEncoder.getPosition() != ptNode.mCachedAddressAfterUpdate) { throw new RuntimeException("Bug: write index is not the same as the cached address " - + "of the node : " + index + " <> " + ptNode.mCachedAddressAfterUpdate); + + "of the node : " + dictEncoder.getPosition() + " <> " + + ptNode.mCachedAddressAfterUpdate); } - ptNodeAddress += getNodeHeaderSize(ptNode, formatOptions); // Sanity checks. if (DBG && ptNode.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) { throw new RuntimeException("A node has a frequency > " + FormatSpec.MAX_TERMINAL_FREQUENCY + " : " + ptNode.mFrequency); } - if (ptNode.mFrequency >= 0) ptNodeAddress += FormatSpec.PTNODE_FREQUENCY_SIZE; - final int childrenOffset = null == ptNode.mChildren - ? FormatSpec.NO_CHILDREN_ADDRESS - : ptNode.mChildren.mCachedAddressAfterUpdate - ptNodeAddress; - buffer[index++] = - makePtNodeFlags(ptNode, ptNodeAddress, childrenOffset, formatOptions); - - if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) { - index = writeParentAddress(buffer, index, parentAddress, formatOptions); - } else { - index = writeParentAddress(buffer, index, parentAddress - + (ptNodeArray.mCachedAddressAfterUpdate - - ptNode.mCachedAddressAfterUpdate), - formatOptions); - } - - index = CharEncoding.writeCharArray(ptNode.mChars, buffer, index); - if (ptNode.hasSeveralChars()) { - buffer[index++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR; - } - if (ptNode.mFrequency >= 0) { - buffer[index++] = (byte) ptNode.mFrequency; - } - - final int shift; - if (formatOptions.mSupportsDynamicUpdate) { - shift = writeVariableSignedAddress(buffer, index, childrenOffset); - } else { - shift = writeVariableAddress(buffer, index, childrenOffset); - } - index += shift; - ptNodeAddress += shift; - - // Write shortcuts - if (null != ptNode.mShortcutTargets && !ptNode.mShortcutTargets.isEmpty()) { - final int indexOfShortcutByteSize = index; - index += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE; - ptNodeAddress += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE; - final Iterator<WeightedString> shortcutIterator = - ptNode.mShortcutTargets.iterator(); - while (shortcutIterator.hasNext()) { - final WeightedString target = shortcutIterator.next(); - ++ptNodeAddress; - int shortcutFlags = makeShortcutFlags(shortcutIterator.hasNext(), - target.mFrequency); - buffer[index++] = (byte)shortcutFlags; - final int shortcutShift = CharEncoding.writeString(buffer, index, target.mWord); - index += shortcutShift; - ptNodeAddress += shortcutShift; - } - final int shortcutByteSize = index - indexOfShortcutByteSize; - if (shortcutByteSize > 0xFFFF) { - throw new RuntimeException("Shortcut list too large"); - } - buffer[indexOfShortcutByteSize] = (byte)(shortcutByteSize >> 8); - buffer[indexOfShortcutByteSize + 1] = (byte)(shortcutByteSize & 0xFF); - } - // Write bigrams - if (null != ptNode.mBigrams) { - final Iterator<WeightedString> bigramIterator = ptNode.mBigrams.iterator(); - while (bigramIterator.hasNext()) { - final WeightedString bigram = bigramIterator.next(); - final PtNode target = - FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord); - final int addressOfBigram = target.mCachedAddressAfterUpdate; - final int unigramFrequencyForThisWord = target.mFrequency; - ++ptNodeAddress; - final int offset = addressOfBigram - ptNodeAddress; - int bigramFlags = makeBigramFlags(bigramIterator.hasNext(), offset, - bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord); - buffer[index++] = (byte)bigramFlags; - final int bigramShift = writeVariableAddress(buffer, index, Math.abs(offset)); - index += bigramShift; - ptNodeAddress += bigramShift; - } - } + dictEncoder.writePtNodeFlags(ptNode, parentPosition, formatOptions); + dictEncoder.writeParentPosition(parentPosition, ptNode, formatOptions); + dictEncoder.writeCharacters(ptNode.mChars, ptNode.hasSeveralChars()); + dictEncoder.writeFrequency(ptNode.mFrequency); + dictEncoder.writeChildrenPosition(ptNode, formatOptions); + dictEncoder.writeShortcuts(ptNode.mShortcutTargets); + dictEncoder.writeBigrams(ptNode.mBigrams, dict); } if (formatOptions.mSupportsDynamicUpdate) { - buffer[index] = buffer[index + 1] = buffer[index + 2] - = FormatSpec.NO_FORWARD_LINK_ADDRESS; - index += FormatSpec.FORWARD_LINK_ADDRESS_SIZE; + dictEncoder.writeForwardLinkAddress(FormatSpec.NO_FORWARD_LINK_ADDRESS); } - if (index != ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize) { - throw new RuntimeException( - "Not the same size : written " + (index - ptNodeArray.mCachedAddressAfterUpdate) + if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate + + ptNodeArray.mCachedSize) { + throw new RuntimeException("Not the same size : written " + + (dictEncoder.getPosition() - ptNodeArray.mCachedAddressAfterUpdate) + " bytes from a node that should have " + ptNodeArray.mCachedSize + " bytes"); } - return index; } /** @@ -862,7 +794,7 @@ public class BinaryDictEncoderUtils { * * @param ptNodeArrays the list of PtNode arrays. */ - private static void showStatistics(ArrayList<PtNodeArray> ptNodeArrays) { + /* package */ static void showStatistics(ArrayList<PtNodeArray> ptNodeArrays) { int firstTerminalAddress = Integer.MAX_VALUE; int lastTerminalAddress = Integer.MIN_VALUE; int size = 0; @@ -912,23 +844,15 @@ public class BinaryDictEncoderUtils { } /** - * Dumps a FusionDictionary to a file. + * Writes a file header to an output stream. * - * @param destination the stream to write the binary data to. + * @param destination the stream to write the file header to. * @param dict the dictionary to write. * @param formatOptions file format options. */ - /* package */ static void writeDictionaryBinary(final OutputStream destination, + /* package */ static void writeDictionaryHeader(final OutputStream destination, final FusionDictionary dict, final FormatOptions formatOptions) - throws IOException, UnsupportedFormatException { - - // Addresses are limited to 3 bytes, but since addresses can be relative to each node - // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding - // the order of the PtNode arrays becomes a quite complicated problem, because though the - // dictionary itself does not have a size limit, each node array must still be within 16MB - // of all its children and parents. As long as this is ensured, the dictionary file may - // grow to any size. - + throws IOException, UnsupportedFormatException { final int version = formatOptions.mVersion; if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) { @@ -975,33 +899,5 @@ public class BinaryDictEncoderUtils { destination.write(bytes); headerBuffer.close(); - - // Leave the choice of the optimal node order to the flattenTree function. - MakedictLog.i("Flattening the tree..."); - ArrayList<PtNodeArray> flatNodes = flattenTree(dict.mRootNodeArray); - - MakedictLog.i("Computing addresses..."); - computeAddresses(dict, flatNodes, formatOptions); - MakedictLog.i("Checking PtNode array..."); - if (DBG) checkFlatPtNodeArrayList(flatNodes); - - // Create a buffer that matches the final dictionary size. - final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1); - final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize; - final byte[] buffer = new byte[bufferSize]; - int index = 0; - - MakedictLog.i("Writing file..."); - int dataEndOffset = 0; - for (PtNodeArray nodeArray : flatNodes) { - dataEndOffset = writePlacedNode(dict, buffer, nodeArray, formatOptions); - } - - if (DBG) showStatistics(flatNodes); - - destination.write(buffer, 0, dataEndOffset); - - destination.close(); - MakedictLog.i("Done"); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java index 89c982e7b..d1589a30e 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java @@ -17,8 +17,11 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.io.IOException; +import java.util.ArrayList; /** * An interface of binary dictionary encoder. @@ -26,4 +29,32 @@ import java.io.IOException; public interface DictEncoder { public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions) throws IOException, UnsupportedFormatException; + + public void setPosition(final int position); + public int getPosition(); + public void writePtNodeCount(final int ptNodeCount); + public void writePtNodeFlags(final PtNode ptNode, final int parentAddress, + final FormatOptions formatOptions); + public void writeParentPosition(final int parentPosition, final PtNode ptNode, + final FormatOptions formatOptions); + public void writeCharacters(final int[] characters, final boolean hasSeveralChars); + public void writeFrequency(final int frequency); + public void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions); + + /** + * Write a shortcut attributes list to memory. + * + * @param shortcuts the shortcut attributes list. + */ + public void writeShortcuts(final ArrayList<WeightedString> shortcuts); + + /** + * Write a bigram attributes list to memory. + * + * @param bigrams the bigram attributes list. + * @param dict the dictionary the node array is a part of (for relative offsets). + */ + public void writeBigrams(final ArrayList<WeightedString> bigrams, final FusionDictionary dict); + + public void writeForwardLinkAddress(final int forwardLinkAddress); } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index b8ef57696..bf35f6a8a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -112,8 +112,10 @@ public final class FormatSpec { * e | 1 byte = bbbbbbbb match * n | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte) * t | otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte - * a | - * ddress + * a | This address is relative to the head of the PtNode. + * d | If the node doesn't have a parent, this field is set to 0. + * d | + * ress * * c | IF FLAG_HAS_MULTIPLE_CHARS * h | char, char, char, char n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers @@ -132,17 +134,18 @@ public final class FormatSpec { * i | 1 byte = bbbbbbbb match * l | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte) * d | otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte - * r | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType - * e | // nothing - * n | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType - * A | children address, 1 byte - * d | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType - * d | children address, 2 bytes - * r | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType - * e | children address, 3 bytes - * s | END - * s - * ress + * r | if this node doesn't have children, this field is set to 0. + * e | (see BinaryDictEncoderUtils#writeVariableSignedAddress) + * n | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType + * a | // nothing + * d | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType + * d | children address, 1 byte + * r | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType + * e | children address, 2 bytes + * s | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType + * s | children address, 3 bytes + * | END + * | This address is relative to the position of this field. * * | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS * | shortcut string list diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java index e81fd4514..3f26ff378 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java @@ -16,13 +16,19 @@ package com.android.inputmethod.latin.makedict; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; /** * An implementation of DictEncoder for version 3 binary dictionary. @@ -31,10 +37,13 @@ public class Ver3DictEncoder implements DictEncoder { private final File mDictFile; private OutputStream mOutStream; + private byte[] mBuffer; + private int mPosition; public Ver3DictEncoder(final File dictFile) { mDictFile = dictFile; mOutStream = null; + mBuffer = null; } // This constructor is used only by BinaryDictOffdeviceUtilsTests. @@ -57,12 +66,172 @@ public class Ver3DictEncoder implements DictEncoder { } @Override - public void writeDictionary(FusionDictionary dict, FormatOptions formatOptions) + public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions) throws IOException, UnsupportedFormatException { + if (formatOptions.mVersion > 3) { + throw new UnsupportedFormatException( + "The given format options has wrong version number : " + + formatOptions.mVersion); + } + if (mOutStream == null) { openStream(); } - BinaryDictEncoderUtils.writeDictionaryBinary(mOutStream, dict, formatOptions); + BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions); + + // Addresses are limited to 3 bytes, but since addresses can be relative to each node + // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding + // the order of the PtNode arrays becomes a quite complicated problem, because though the + // dictionary itself does not have a size limit, each node array must still be within 16MB + // of all its children and parents. As long as this is ensured, the dictionary file may + // grow to any size. + + // Leave the choice of the optimal node order to the flattenTree function. + MakedictLog.i("Flattening the tree..."); + ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray); + + MakedictLog.i("Computing addresses..."); + BinaryDictEncoderUtils.computeAddresses(dict, flatNodes, formatOptions); + MakedictLog.i("Checking PtNode array..."); + if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes); + + // Create a buffer that matches the final dictionary size. + final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1); + final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize; + mBuffer = new byte[bufferSize]; + + MakedictLog.i("Writing file..."); + + for (PtNodeArray nodeArray : flatNodes) { + BinaryDictEncoderUtils.writePlacedNode(dict, this, nodeArray, formatOptions); + } + if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes); + mOutStream.write(mBuffer, 0, mPosition); + + MakedictLog.i("Done"); close(); } + + @Override + public void setPosition(final int position) { + if (mBuffer == null || position < 0 || position >= mBuffer.length) return; + mPosition = position; + } + + @Override + public int getPosition() { + return mPosition; + } + + @Override + public void writePtNodeCount(final int ptNodeCount) { + final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount); + if (1 == countSize) { + mBuffer[mPosition++] = (byte) ptNodeCount; + } else if (2 == countSize) { + mBuffer[mPosition++] = (byte) ((ptNodeCount >> 8) & 0xFF); + mBuffer[mPosition++] = (byte) (ptNodeCount & 0xFF); + } else { + throw new RuntimeException("Strange size from getGroupCountSize : " + countSize); + } + } + + @Override + public void writePtNodeFlags(final PtNode ptNode, final int parentAddress, + final FormatOptions formatOptions) { + final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions); + mBuffer[mPosition++] = BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mPosition, + childrenPos, formatOptions); + } + + @Override + public void writeParentPosition(final int parentPosition, final PtNode ptNode, + final FormatOptions formatOptions) { + if (parentPosition == FormatSpec.NO_PARENT_ADDRESS) { + mPosition = BinaryDictEncoderUtils.writeParentAddress(mBuffer, mPosition, + parentPosition, formatOptions); + } else { + mPosition = BinaryDictEncoderUtils.writeParentAddress(mBuffer, mPosition, + parentPosition - ptNode.mCachedAddressAfterUpdate, formatOptions); + } + } + + @Override + public void writeCharacters(final int[] codePoints, final boolean hasSeveralChars) { + mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition); + if (hasSeveralChars) { + mBuffer[mPosition++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR; + } + } + + @Override + public void writeFrequency(final int frequency) { + if (frequency >= 0) { + mBuffer[mPosition++] = (byte) frequency; + } + } + + @Override + public void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) { + final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions); + if (formatOptions.mSupportsDynamicUpdate) { + mPosition += BinaryDictEncoderUtils.writeSignedChildrenPosition(mBuffer, mPosition, + childrenPos); + } else { + mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition, + childrenPos); + } + } + + @Override + public void writeShortcuts(final ArrayList<WeightedString> shortcuts) { + if (null == shortcuts || shortcuts.isEmpty()) return; + + final int indexOfShortcutByteSize = mPosition; + mPosition += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE; + final Iterator<WeightedString> shortcutIterator = shortcuts.iterator(); + while (shortcutIterator.hasNext()) { + final WeightedString target = shortcutIterator.next(); + final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags( + shortcutIterator.hasNext(), + target.mFrequency); + mBuffer[mPosition++] = (byte)shortcutFlags; + final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord); + mPosition += shortcutShift; + } + final int shortcutByteSize = mPosition - indexOfShortcutByteSize; + if (shortcutByteSize > 0xFFFF) { + throw new RuntimeException("Shortcut list too large"); + } + mBuffer[indexOfShortcutByteSize] = (byte)((shortcutByteSize >> 8) & 0xFF); + mBuffer[indexOfShortcutByteSize + 1] = (byte)(shortcutByteSize & 0xFF); + } + + @Override + public void writeBigrams(final ArrayList<WeightedString> bigrams, final FusionDictionary dict) { + if (bigrams == null) return; + + final Iterator<WeightedString> bigramIterator = bigrams.iterator(); + while (bigramIterator.hasNext()) { + final WeightedString bigram = bigramIterator.next(); + final PtNode target = + FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord); + final int addressOfBigram = target.mCachedAddressAfterUpdate; + final int unigramFrequencyForThisWord = target.mFrequency; + final int offset = addressOfBigram + - (mPosition + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); + int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(), + offset, bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord); + mBuffer[mPosition++] = (byte) bigramFlags; + mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition, + Math.abs(offset)); + } + } + + @Override + public void writeForwardLinkAddress(final int forwardLinkAddress) { + mBuffer[mPosition++] = (byte) ((forwardLinkAddress >> 16) & 0xFF); + mBuffer[mPosition++] = (byte) ((forwardLinkAddress >> 8) & 0xFF); + mBuffer[mPosition++] = (byte) (forwardLinkAddress & 0xFF); + } } diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java index d44660623..e43e74d87 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.AbstractDictionaryWriter; import com.android.inputmethod.latin.ExpandableDictionary; @@ -156,4 +157,10 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr public boolean isValidWord(final String word) { return mExpandableDictionary.isValidWord(word); } + + @UsedForTesting + public boolean isInDictionaryForTests(final String word) { + // TODO: Use native method to determine whether the word is in dictionary or not + return mBigramList.containsKey(word); + } } diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java index a08145b33..5b1d0647b 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java @@ -69,7 +69,7 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableBinaryDi mPrefs = sp; if (mLocale != null && mLocale.length() > 1) { asyncLoadDictionaryToMemory(); - asyncReloadDictionaryIfRequired(); + reloadDictionaryIfRequired(); } } @@ -196,12 +196,6 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableBinaryDi return mLocale; } - @UsedForTesting - /* package for test */ void forceAddWordForTest( - final String word0, final String word1, final boolean isValid) { - addToPersonalizationPredictionDictionary(word0, word1, isValid); - } - public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) { session.setPredictionDictionary(this); mSessions.add(session); diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index c8deaf90d..5f702ee3f 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -52,7 +52,7 @@ public class PersonalizationHelper { if (DEBUG) { Log.w(TAG, "Use cached UserHistoryPredictionDictionary for " + locale); } - dict.asyncReloadDictionaryIfRequired(); + dict.reloadDictionaryIfRequired(); return dict; } } diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java index 6c2c9e26e..4c1803bdf 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java @@ -97,6 +97,10 @@ public final class UserHistoryDictionaryBigramList { return mBigramMap.isEmpty(); } + public boolean containsKey(String word) { + return mBigramMap.containsKey(word); + } + public Set<String> keySet() { return mBigramMap.keySet(); } diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index fd83865ba..8732a59f8 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -44,7 +44,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_VIBRATE_ON = "vibrate_on"; public static final String PREF_SOUND_ON = "sound_on"; public static final String PREF_POPUP_ON = "popup_on"; - public static final String PREF_VOICE_MODE = "voice_mode"; + // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead. + public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode"; + public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key"; public static final String PREF_CORRECTION_SETTINGS = "correction_settings"; public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary"; public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key"; diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index 1677e1828..cb7dda655 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -61,7 +61,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */; - private ListPreference mVoicePreference; + private CheckBoxPreference mVoiceInputKeyPreference; private ListPreference mShowCorrectionSuggestionsPreference; private ListPreference mAutoCorrectionThresholdPreference; private ListPreference mKeyPreviewPopupDismissDelay; @@ -107,7 +107,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment SubtypeLocaleUtils.init(context); AudioAndHapticFeedbackManager.init(context); - mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE); + mVoiceInputKeyPreference = + (CheckBoxPreference) findPreference(Settings.PREF_VOICE_INPUT_KEY); mShowCorrectionSuggestionsPreference = (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING); final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); @@ -166,7 +167,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); if (!showVoiceKeyOption) { - generalSettings.removePreference(mVoicePreference); + generalSettings.removePreference(mVoiceInputKeyPreference); } final PreferenceGroup advancedSettings = @@ -243,10 +244,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment public void onResume() { super.onResume(); final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); - if (isShortcutImeEnabled) { - updateVoiceModeSummary(); - } else { - getPreferenceScreen().removePreference(mVoicePreference); + if (!isShortcutImeEnabled) { + getPreferenceScreen().removePreference(mVoiceInputKeyPreference); } final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); final CheckBoxPreference showSetupWizardIcon = @@ -287,7 +286,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); } ensureConsistencyOfAutoCorrectionSettings(); - updateVoiceModeSummary(); updateShowCorrectionSuggestionsSummary(); updateKeyPreviewPopupDelaySummary(); refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); @@ -330,12 +328,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]); } - private void updateVoiceModeSummary() { - mVoicePreference.setSummary( - getResources().getStringArray(R.array.voice_input_modes_summary) - [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); - } - private void refreshEnablingsOfKeypressSoundAndVibrationSettings( final SharedPreferences sp, final Resources res) { setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS, diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 32730d23f..072bb8731 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -64,7 +64,7 @@ public final class SettingsValues { public final boolean mVibrateOn; public final boolean mSoundOn; public final boolean mKeyPreviewPopupOn; - private final String mVoiceMode; + private final boolean mShowsVoiceInputKey; public final boolean mIncludesOtherImesInLanguageSwitchList; public final boolean mShowsLanguageSwitchKey; public final boolean mUseContactsDict; @@ -90,8 +90,6 @@ public final class SettingsValues { public final float mAutoCorrectionThreshold; public final boolean mCorrectionEnabled; public final int mSuggestionVisibility; - private final boolean mVoiceKeyEnabled; - private final boolean mVoiceKeyOnMain; public final boolean mBoostPersonalizationDictionaryForDebug; public final boolean mUseOnlyPersonalizationDictionaryForDebug; @@ -137,9 +135,7 @@ public final class SettingsValues { mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); mSlidingKeyInputPreviewEnabled = prefs.getBoolean( Settings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); - final String voiceModeMain = res.getString(R.string.voice_mode_main); - final String voiceModeOff = res.getString(R.string.voice_mode_off); - mVoiceMode = prefs.getString(Settings.PREF_VOICE_MODE, voiceModeMain); + mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); @@ -159,8 +155,6 @@ public final class SettingsValues { mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res); mAutoCorrectionThreshold = readAutoCorrectionThreshold(res, autoCorrectionThresholdRawValue); - mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff); - mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); mGestureFloatingPreviewTextEnabled = prefs.getBoolean( @@ -201,7 +195,7 @@ public final class SettingsValues { mSoundOn = true; mKeyPreviewPopupOn = true; mSlidingKeyInputPreviewEnabled = true; - mVoiceMode = "0"; + mShowsVoiceInputKey = true; mIncludesOtherImesInLanguageSwitchList = false; mShowsLanguageSwitchKey = true; mUseContactsDict = true; @@ -214,8 +208,6 @@ public final class SettingsValues { mKeypressSoundVolume = 1; mKeyPreviewPopupDismissDelay = 70; mAutoCorrectionThreshold = 1; - mVoiceKeyEnabled = true; - mVoiceKeyOnMain = true; mGestureInputEnabled = true; mGestureTrailEnabled = true; mGestureFloatingPreviewTextEnabled = true; @@ -274,14 +266,10 @@ public final class SettingsValues { public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) { final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; - return shortcutImeEnabled && mVoiceKeyEnabled + return shortcutImeEnabled && mShowsVoiceInputKey && !InputTypeUtils.isPasswordInputType(inputType); } - public boolean isVoiceKeyOnMain() { - return mVoiceKeyOnMain; - } - public boolean isLanguageSwitchKeyEnabled() { if (!mShowsLanguageSwitchKey) { return false; @@ -372,4 +360,18 @@ public final class SettingsValues { } return autoCorrectionThreshold; } + + private static boolean needsToShowVoiceInputKey(SharedPreferences prefs, Resources res) { + final String voiceModeMain = res.getString(R.string.voice_mode_main); + final String voiceMode = prefs.getString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain); + final boolean showsVoiceInputKey = voiceMode == null || voiceMode.equals(voiceModeMain); + if (!showsVoiceInputKey) { + // Migrate settings from PREF_VOICE_MODE_OBSOLETE to PREF_VOICE_INPUT_KEY + // Set voiceModeMain as a value of obsolete voice mode settings. + prefs.edit().putString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain).apply(); + // Disable voice input key. + prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, false).apply(); + } + return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java new file mode 100644 index 000000000..c2e97a36f --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 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.utils; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This class is a holder of a result of asynchronous computation. + * + * @param <E> the type of the result. + */ +public class AsyncResultHolder<E> { + + private final Object mLock = new Object(); + + private E mResult; + private final CountDownLatch mLatch; + + public AsyncResultHolder() { + mLatch = new CountDownLatch(1); + } + + /** + * Sets the result value to this holder. + * + * @param result the value which is set. + */ + public void set(final E result) { + synchronized(mLock) { + if (mLatch.getCount() > 0) { + mResult = result; + mLatch.countDown(); + } + } + } + + /** + * Gets the result value held in this holder. + * Causes the current thread to wait unless the value is set or the specified time is elapsed. + * + * @param defaultValue the default value. + * @param timeOut the time to wait. + * @return if the result is set until the time limit then the result, otherwise defaultValue. + */ + public E get(final E defaultValue, final long timeOut) { + try { + if(mLatch.await(timeOut, TimeUnit.MILLISECONDS)) { + return mResult; + } else { + return defaultValue; + } + } catch (InterruptedException e) { + return defaultValue; + } + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java index c4ead0ad1..ac654fa65 100644 --- a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java @@ -65,12 +65,12 @@ public final class DebugLogUtils { /** * Get the stack trace contained in an exception as a human-readable string. - * @param e the exception + * @param t the throwable * @return the human-readable stack trace */ - public static String getStackTrace(final Exception e) { + public static String getStackTrace(final Throwable t) { final StringBuilder sb = new StringBuilder(); - final StackTraceElement[] frames = e.getStackTrace(); + final StackTraceElement[] frames = t.getStackTrace(); for (int j = 0; j < frames.length; ++j) { sb.append(frames[j].toString() + "\n"); } diff --git a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java new file mode 100644 index 000000000..3c1db6529 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 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.utils; + +import java.util.ArrayDeque; +import java.util.Queue; + +/** + * An object that executes submitted tasks using a thread. + */ +public class PrioritizedSerialExecutor { + public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName(); + + private final Object mLock = new Object(); + + // The default value of capacities of task queues. + private static final int TASK_QUEUE_CAPACITY = 1000; + private final Queue<Runnable> mTasks; + private final Queue<Runnable> mPrioritizedTasks; + + // The task which is running now. + private Runnable mActive; + + public PrioritizedSerialExecutor() { + mTasks = new ArrayDeque<Runnable>(TASK_QUEUE_CAPACITY); + mPrioritizedTasks = new ArrayDeque<Runnable>(TASK_QUEUE_CAPACITY); + } + + /** + * Clears all queued tasks. + */ + public void clearAllTasks() { + synchronized(mLock) { + mTasks.clear(); + mPrioritizedTasks.clear(); + } + } + + /** + * Enqueues the given task into the task queue. + * @param r the enqueued task + */ + public void execute(final Runnable r) { + synchronized(mLock) { + mTasks.offer(r); + if (mActive == null) { + scheduleNext(); + } + } + } + + /** + * Enqueues the given task into the prioritized task queue. + * @param r the enqueued task + */ + public void executePrioritized(final Runnable r) { + synchronized(mLock) { + mPrioritizedTasks.offer(r); + if (mActive == null) { + scheduleNext(); + } + } + } + + private boolean fetchNextTasks() { + synchronized(mLock) { + mActive = mPrioritizedTasks.poll(); + if (mActive == null) { + mActive = mTasks.poll(); + } + return mActive != null; + } + } + + private void scheduleNext() { + synchronized(mLock) { + if (!fetchNextTasks()) { + return; + } + new Thread(new Runnable() { + @Override + public void run() { + try { + do { + synchronized(mLock) { + if (mActive != null) { + mActive.run(); + } + } + } while (fetchNextTasks()); + } finally { + scheduleNext(); + } + } + }).start(); + } + } + + public void remove(final Runnable r) { + synchronized(mLock) { + mTasks.remove(r); + mPrioritizedTasks.remove(r); + } + } + + public void replaceAndExecute(final Runnable oldTask, final Runnable newTask) { + synchronized(mLock) { + if (oldTask != null) remove(oldTask); + execute(newTask); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java index 99788f6f2..05f3061a8 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; import java.util.TreeMap; +import java.util.concurrent.TimeUnit; /** * Reads and writes Binary files for a UserHistoryDictionary. @@ -43,6 +44,9 @@ import java.util.TreeMap; public final class UserHistoryDictIOUtils { private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName(); private static final boolean DEBUG = false; + private static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE"; + private static final String USES_FORGETTING_CURVE_VALUE = "1"; + private static final String LAST_UPDATED_TIME_KEY = "date"; public interface OnAddWordListener { public void setUnigram(final String word, final String shortcutTarget, final int frequency); @@ -61,6 +65,9 @@ public final class UserHistoryDictIOUtils { final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams, final FormatOptions formatOptions) { final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams); + fusionDict.addOptionAttribute(USES_FORGETTING_CURVE_KEY, USES_FORGETTING_CURVE_VALUE); + fusionDict.addOptionAttribute(LAST_UPDATED_TIME_KEY, + String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); try { dictEncoder.writeDictionary(fusionDict, formatOptions); Log.d(TAG, "end writing"); diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 0e36f3df9..d83bdadfa 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -78,6 +78,7 @@ LATIN_IME_CORE_SRC_FILES := \ dynamic_patricia_trie_reading_helper.cpp \ dynamic_patricia_trie_reading_utils.cpp \ dynamic_patricia_trie_writing_helper.cpp \ + dynamic_patricia_trie_writing_utils.cpp \ patricia_trie_policy.cpp \ patricia_trie_reading_utils.cpp) \ $(addprefix suggest/policyimpl/dictionary/utils/, \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 86c2394d1..8da1859c4 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -204,6 +204,7 @@ static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, } jsize wordLength = env->GetArrayLength(word); int codePoints[wordLength]; + env->GetIntArrayRegion(word, 0, wordLength, codePoints); dictionary->addUnigramWord(codePoints, wordLength, probability); } @@ -215,8 +216,10 @@ static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, } jsize word0Length = env->GetArrayLength(word0); int word0CodePoints[word0Length]; + env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); jsize word1Length = env->GetArrayLength(word1); int word1CodePoints[word1Length]; + env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints, word1Length, probability); } @@ -229,8 +232,10 @@ static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass claz } jsize word0Length = env->GetArrayLength(word0); int word0CodePoints[word0Length]; + env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); jsize word1Length = env->GetArrayLength(word1); int word1CodePoints[word1Length]; + env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); dictionary->removeBigramWords(word0CodePoints, word0Length, word1CodePoints, word1Length); } diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index bb54e608e..e81591992 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -21,7 +21,6 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/multi_bigram_map.h" -#include "suggest/core/dictionary/probability_utils.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "utils/char_utils.h" @@ -93,13 +92,15 @@ namespace latinime { if (NOT_A_VALID_WORD_POS == wordPos || NOT_A_VALID_WORD_POS == prevWordPos) { // Note: Normally wordPos comes from the dictionary and should never equal // NOT_A_VALID_WORD_POS. - return ProbabilityUtils::backoff(unigramProbability); + return dictionaryStructurePolicy->getProbability(unigramProbability, + NOT_A_PROBABILITY); } if (multiBigramMap) { return multiBigramMap->getBigramProbability(dictionaryStructurePolicy, prevWordPos, wordPos, unigramProbability); } - return ProbabilityUtils::backoff(unigramProbability); + return dictionaryStructurePolicy->getProbability(unigramProbability, + NOT_A_PROBABILITY); } //////////////// diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h index f437c95f6..9bc96877e 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h @@ -116,10 +116,6 @@ class DicNodeStatePrevWord { return mPrevWordStart; } - int16_t getPrevWordProbability() const { - return mPrevWordProbability; - } - int getPrevWordNodePos() const { return mPrevWordNodePos; } diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp index e74a1dbc8..cf1cd8815 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp @@ -23,7 +23,6 @@ #include "defines.h" #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" #include "suggest/core/dictionary/dictionary.h" -#include "suggest/core/dictionary/probability_utils.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "utils/char_utils.h" @@ -131,7 +130,7 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng // resulting probability is 8 - although in the practice it's never bigger than 3 or 4 // in very bad cases. This means that sometimes, we'll see some bigrams interverted // here, but it can't get too bad. - const int probability = ProbabilityUtils::computeProbabilityForBigram( + const int probability = mDictionaryStructurePolicy->getProbability( unigramProbability, bigramsIt.getProbability()); addWordBigram(bigramBuffer, codePointCount, probability, outBigramProbability, outBigramCodePoints, outputTypes); diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 8418a608a..02ece639c 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -90,7 +90,7 @@ int Dictionary::getProbability(const int *word, int length) const { if (NOT_A_VALID_WORD_POS == pos) { return NOT_A_PROBABILITY; } - return getDictionaryStructurePolicy()->getUnigramProbability(pos); + return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos); } bool Dictionary::isValidBigram(const int *word0, int length0, const int *word1, int length1) const { diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h index fb4a80083..9efe5f6f9 100644 --- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h @@ -22,7 +22,6 @@ #include "defines.h" #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" #include "suggest/core/dictionary/bloom_filter.h" -#include "suggest/core/dictionary/probability_utils.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "utils/hash_map_compat.h" @@ -43,11 +42,12 @@ class MultiBigramMap { hash_map_compat<int, BigramMap>::const_iterator mapPosition = mBigramMaps.find(wordPosition); if (mapPosition != mBigramMaps.end()) { - return mapPosition->second.getBigramProbability(nextWordPosition, unigramProbability); + return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition, + unigramProbability); } if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) { addBigramsForWordPosition(structurePolicy, wordPosition); - return mBigramMaps[wordPosition].getBigramProbability( + return mBigramMaps[wordPosition].getBigramProbability(structurePolicy, nextWordPosition, unigramProbability); } return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition, @@ -82,17 +82,17 @@ class MultiBigramMap { } AK_FORCE_INLINE int getBigramProbability( + const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nextWordPosition, const int unigramProbability) const { + int bigramProbability = NOT_A_PROBABILITY; if (mBloomFilter.isInFilter(nextWordPosition)) { const hash_map_compat<int, int>::const_iterator bigramProbabilityIt = mBigramMap.find(nextWordPosition); if (bigramProbabilityIt != mBigramMap.end()) { - const int bigramProbability = bigramProbabilityIt->second; - return ProbabilityUtils::computeProbabilityForBigram( - unigramProbability, bigramProbability); + bigramProbability = bigramProbabilityIt->second; } } - return ProbabilityUtils::backoff(unigramProbability); + return structurePolicy->getProbability(unigramProbability, bigramProbability); } private: @@ -111,17 +111,18 @@ class MultiBigramMap { AK_FORCE_INLINE int readBigramProbabilityFromBinaryDictionary( const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos, const int nextWordPosition, const int unigramProbability) { + int bigramProbability = NOT_A_PROBABILITY; const int bigramsListPos = structurePolicy->getBigramsPositionOfNode(nodePos); BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), bigramsListPos); while (bigramsIt.hasNext()) { bigramsIt.next(); if (bigramsIt.getBigramPos() == nextWordPosition) { - return ProbabilityUtils::computeProbabilityForBigram( - unigramProbability, bigramsIt.getProbability()); + bigramProbability = bigramsIt.getProbability(); + break; } } - return ProbabilityUtils::backoff(unigramProbability); + return structurePolicy->getProbability(unigramProbability, bigramProbability); } static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP; diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h index 532411509..c8cbbcfdf 100644 --- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h @@ -47,7 +47,10 @@ class DictionaryStructureWithBufferPolicy { virtual int getTerminalNodePositionOfWord(const int *const inWord, const int length, const bool forceLowerCaseSearch) const = 0; - virtual int getUnigramProbability(const int nodePos) const = 0; + virtual int getProbability(const int unigramProbability, + const int bigramProbability) const = 0; + + virtual int getUnigramProbabilityOfPtNode(const int nodePos) const = 0; virtual int getShortcutPositionOfNode(const int nodePos) const = 0; diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 7d8dd21c5..e788e914a 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -171,7 +171,9 @@ int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequen terminalIndex, doubleLetterTerminalIndex, doubleLetterLevel); const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight) + doubleLetterCost; - const bool isPossiblyOffensiveWord = terminalDicNode->getProbability() <= 0; + const bool isPossiblyOffensiveWord = + traverseSession->getDictionaryStructurePolicy()->getProbability( + terminalDicNode->getProbability(), NOT_A_PROBABILITY) <= 0; const bool isExactMatch = terminalDicNode->isExactMatch(); const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase(); // Heuristic: We exclude freq=0 first-char-uppercase words from exact match. diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp index e31a91069..936dc9c5d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp @@ -20,12 +20,13 @@ namespace latinime { bool DynamicBigramListPolicy::copyAllBigrams(int *const fromPos, int *const toPos) { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*fromPos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { *fromPos -= mBuffer->getOriginalBufferSize(); } BigramListReadWriteUtils::BigramFlags flags; do { + // The buffer address can be changed after calling buffer writing methods. + const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); flags = BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, fromPos); int bigramPos = BigramListReadWriteUtils::getBigramAddressAndForwardPointer( buffer, flags, fromPos); @@ -63,7 +64,6 @@ bool DynamicBigramListPolicy::copyAllBigrams(int *const fromPos, int *const toPo bool DynamicBigramListPolicy::addBigramEntry(const int bigramPos, const int probability, int *const pos) { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { *pos -= mBuffer->getOriginalBufferSize(); } @@ -73,6 +73,8 @@ bool DynamicBigramListPolicy::addBigramEntry(const int bigramPos, const int prob if (usesAdditionalBuffer) { entryPos += mBuffer->getOriginalBufferSize(); } + // The buffer address can be changed after calling buffer writing methods. + const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); flags = BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, pos); BigramListReadWriteUtils::getBigramAddressAndForwardPointer(buffer, flags, pos); if (BigramListReadWriteUtils::hasNext(flags)) { @@ -118,13 +120,14 @@ bool DynamicBigramListPolicy::addBigramEntry(const int bigramPos, const int prob bool DynamicBigramListPolicy::removeBigram(const int bigramListPos, const int targetBigramPos) { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(bigramListPos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); int pos = bigramListPos; if (usesAdditionalBuffer) { pos -= mBuffer->getOriginalBufferSize(); } BigramListReadWriteUtils::BigramFlags flags; do { + // The buffer address can be changed after calling buffer writing methods. + const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); flags = BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, &pos); int bigramOffsetFieldPos = pos; if (usesAdditionalBuffer) { @@ -139,8 +142,7 @@ bool DynamicBigramListPolicy::removeBigram(const int bigramListPos, const int ta continue; } // Target entry is found. Write 0 into the bigram pos field to mark the bigram invalid. - const int bigramOffsetFieldSize = - BigramListReadWriteUtils::attributeAddressSize(flags); + const int bigramOffsetFieldSize = BigramListReadWriteUtils::attributeAddressSize(flags); if (!mBuffer->writeUintAndAdvancePosition(0 /* data */, bigramOffsetFieldSize, &bigramOffsetFieldPos)) { return false; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp index 6bb90fc2d..5674cb48e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp @@ -34,7 +34,7 @@ void DynamicPatriciaTrieNodeReader::fetchNodeInfoFromBufferAndProcessMovedNode(c mFlags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); const int parentPos = DynamicPatriciaTrieReadingUtils::getParentPosAndAdvancePosition(dictBuf, &pos); - mParentPos = (parentPos != 0) ? mNodePos + parentPos : NOT_A_DICT_POS; + mParentPos = (parentPos != 0) ? nodePos + parentPos : NOT_A_DICT_POS; if (outCodePoints != 0) { mCodePointCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition( dictBuf, mFlags, maxCodePointCount, outCodePoints, &pos); @@ -43,10 +43,19 @@ void DynamicPatriciaTrieNodeReader::fetchNodeInfoFromBufferAndProcessMovedNode(c dictBuf, mFlags, MAX_WORD_LENGTH, &pos); } if (isTerminal()) { + mProbabilityFieldPos = pos; + if (usesAdditionalBuffer) { + mProbabilityFieldPos += mBuffer->getOriginalBufferSize(); + } mProbability = PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictBuf, &pos); } else { + mProbabilityFieldPos = NOT_A_DICT_POS; mProbability = NOT_A_PROBABILITY; } + mChildrenPosFieldPos = pos; + if (usesAdditionalBuffer) { + mChildrenPosFieldPos += mBuffer->getOriginalBufferSize(); + } mChildrenPos = DynamicPatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition( dictBuf, mFlags, &pos); if (usesAdditionalBuffer && mChildrenPos != NOT_A_DICT_POS) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h index acc68b321..2ee7c2495 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h @@ -40,9 +40,11 @@ class DynamicPatriciaTrieNodeReader { const DictionaryShortcutsStructurePolicy *const shortcutsPolicy) : mBuffer(buffer), mBigramsPolicy(bigramsPolicy), mShortcutsPolicy(shortcutsPolicy), mNodePos(NOT_A_VALID_WORD_POS), mFlags(0), - mParentPos(NOT_A_DICT_POS), mCodePointCount(0), mProbability(NOT_A_PROBABILITY), - mChildrenPos(NOT_A_DICT_POS), mShortcutPos(NOT_A_DICT_POS), - mBigramPos(NOT_A_DICT_POS), mSiblingPos(NOT_A_VALID_WORD_POS) {} + mParentPos(NOT_A_DICT_POS), mCodePointCount(0), + mProbabilityFieldPos(NOT_A_DICT_POS), mProbability(NOT_A_PROBABILITY), + mChildrenPosFieldPos(NOT_A_DICT_POS), mChildrenPos(NOT_A_DICT_POS), + mShortcutPos(NOT_A_DICT_POS), mBigramPos(NOT_A_DICT_POS), + mSiblingPos(NOT_A_VALID_WORD_POS) {} ~DynamicPatriciaTrieNodeReader() {} @@ -95,11 +97,19 @@ class DynamicPatriciaTrieNodeReader { } // Probability + AK_FORCE_INLINE int getProbabilityFieldPos() const { + return mProbabilityFieldPos; + } + AK_FORCE_INLINE int getProbability() const { return mProbability; } - // Children node group position + // Children PtNode array position + AK_FORCE_INLINE int getChildrenPosFieldPos() const { + return mChildrenPosFieldPos; + } + AK_FORCE_INLINE int getChildrenPos() const { return mChildrenPos; } @@ -129,7 +139,9 @@ class DynamicPatriciaTrieNodeReader { DynamicPatriciaTrieReadingUtils::NodeFlags mFlags; int mParentPos; uint8_t mCodePointCount; + int mProbabilityFieldPos; int mProbability; + int mChildrenPosFieldPos; int mChildrenPos; int mShortcutPos; int mBigramPos; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp index 3b9878b82..945677b50 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp @@ -24,6 +24,7 @@ #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" namespace latinime { @@ -134,7 +135,20 @@ int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const in return NOT_A_VALID_WORD_POS; } -int DynamicPatriciaTriePolicy::getUnigramProbability(const int nodePos) const { +int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability, + const int bigramProbability) const { + // TODO: check mHeaderPolicy.usesForgettingCurve(); + if (unigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else if (bigramProbability == NOT_A_PROBABILITY) { + return ProbabilityUtils::backoff(unigramProbability); + } else { + return ProbabilityUtils::computeProbabilityForBigram(unigramProbability, + bigramProbability); + } +} + +int DynamicPatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const { if (nodePos == NOT_A_VALID_WORD_POS) { return NOT_A_PROBABILITY; } @@ -144,7 +158,7 @@ int DynamicPatriciaTriePolicy::getUnigramProbability(const int nodePos) const { if (nodeReader.isDeleted() || nodeReader.isBlacklisted() || nodeReader.isNotAWord()) { return NOT_A_PROBABILITY; } - return nodeReader.getProbability(); + return getProbability(nodeReader.getProbability(), NOT_A_PROBABILITY); } int DynamicPatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const { diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h index 5873d3d65..cdab0e16a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h @@ -57,7 +57,9 @@ class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getTerminalNodePositionOfWord(const int *const inWord, const int length, const bool forceLowerCaseSearch) const; - int getUnigramProbability(const int nodePos) const; + int getProbability(const int unigramProbability, const int bigramProbability) const; + + int getUnigramProbabilityOfPtNode(const int nodePos) const; int getShortcutPositionOfNode(const int nodePos) const; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp index 2042fcbd2..a0b5be6a4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp @@ -70,9 +70,10 @@ void DynamicPatriciaTrieReadingHelper::followForwardLink() { if (usesAdditionalBuffer) { mPos += mBuffer->getOriginalBufferSize(); } + mPosOfLastForwardLinkField = mPos; if (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) { // Follow the forward link. - mPos = forwardLinkPosition; + mPos += forwardLinkPosition; nextNodeArray(); } else { // All node arrays have been read. diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h index b108ed5fb..db1c392bb 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h @@ -38,8 +38,8 @@ class DynamicPatriciaTrieReadingHelper { const DictionaryBigramsStructurePolicy *const bigramsPolicy, const DictionaryShortcutsStructurePolicy *const shortcutsPolicy) : mIsError(false), mPos(NOT_A_DICT_POS), mNodeCount(0), mPrevTotalCodePointCount(0), - mTotalNodeCount(0), mNodeArrayCount(0), mBuffer(buffer), - mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy) {} + mTotalNodeCount(0), mNodeArrayCount(0), mPosOfLastForwardLinkField(NOT_A_DICT_POS), + mBuffer(buffer), mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy) {} ~DynamicPatriciaTrieReadingHelper() {} @@ -62,6 +62,7 @@ class DynamicPatriciaTrieReadingHelper { mPrevTotalCodePointCount = 0; mTotalNodeCount = 0; mNodeArrayCount = 0; + mPosOfLastForwardLinkField = NOT_A_DICT_POS; nextNodeArray(); if (!isEnd()) { fetchNodeInfo(); @@ -81,6 +82,7 @@ class DynamicPatriciaTrieReadingHelper { mPrevTotalCodePointCount = 0; mTotalNodeCount = 1; mNodeArrayCount = 1; + mPosOfLastForwardLinkField = NOT_A_DICT_POS; fetchNodeInfo(); } } @@ -140,6 +142,7 @@ class DynamicPatriciaTrieReadingHelper { mTotalNodeCount = 0; mNodeArrayCount = 0; mPos = mNodeReader.getChildrenPos(); + mPosOfLastForwardLinkField = NOT_A_DICT_POS; // Read children node array. nextNodeArray(); if (!isEnd()) { @@ -158,12 +161,17 @@ class DynamicPatriciaTrieReadingHelper { mNodeArrayCount = 1; mNodeCount = 1; mPos = mNodeReader.getParentPos(); + mPosOfLastForwardLinkField = NOT_A_DICT_POS; fetchNodeInfo(); } else { mPos = NOT_A_DICT_POS; } } + AK_FORCE_INLINE int getPosOfLastForwardLinkField() const { + return mPosOfLastForwardLinkField; + } + private: DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieReadingHelper); @@ -177,6 +185,7 @@ class DynamicPatriciaTrieReadingHelper { int mPrevTotalCodePointCount; int mTotalNodeCount; int mNodeArrayCount; + int mPosOfLastForwardLinkField; const BufferWithExtendableBuffer *const mBuffer; DynamicPatriciaTrieNodeReader mNodeReader; int mMergedNodeCodePoints[MAX_WORD_LENGTH]; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h index a6cb46d39..62d73bb02 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h @@ -56,6 +56,15 @@ class DynamicPatriciaTrieReadingUtils { return FLAG_IS_DELETED == (MASK_MOVED & flags); } + static AK_FORCE_INLINE NodeFlags updateAndGetFlags(const NodeFlags originalFlags, + const bool isMoved, const bool isDeleted) { + NodeFlags flags = originalFlags; + flags = isMoved ? ((flags & (!MASK_MOVED)) | FLAG_IS_MOVED) : flags; + flags = isDeleted ? ((flags & (!MASK_MOVED)) | FLAG_IS_DELETED) : flags; + flags = (!isMoved && !isDeleted) ? ((flags & (!MASK_MOVED)) | FLAG_IS_NOT_MOVED) : flags; + return flags; + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieReadingUtils); diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp index 128d69d88..99a983f21 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp @@ -19,6 +19,9 @@ #include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" +#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h" namespace latinime { @@ -26,6 +29,7 @@ namespace latinime { bool DynamicPatriciaTrieWritingHelper::addUnigramWord( DynamicPatriciaTrieReadingHelper *const readingHelper, const int *const wordCodePoints, const int codePointCount, const int probability) { + int parentPos = NOT_A_VALID_WORD_POS; while (!readingHelper->isEnd()) { const int matchedCodePointCount = readingHelper->getPrevTotalCodePointCount(); if (!readingHelper->isMatchedCodePoint(0 /* index */, @@ -40,38 +44,37 @@ bool DynamicPatriciaTrieWritingHelper::addUnigramWord( const int nodeCodePointCount = nodeReader->getCodePointCount(); for (int j = 1; j < nodeCodePointCount; ++j) { const int nextIndex = matchedCodePointCount + j; - if (nextIndex >= codePointCount) { - // TODO: split current node after j - 1, create child and make this terminal. - return false; - } - if (!readingHelper->isMatchedCodePoint(j, + if (nextIndex >= codePointCount || !readingHelper->isMatchedCodePoint(j, wordCodePoints[matchedCodePointCount + j])) { - // TODO: split current node after j - 1 and create two children. - return false; + return reallocatePtNodeAndAddNewPtNodes(nodeReader, + readingHelper->getMergedNodeCodePoints(), j, probability, + wordCodePoints + matchedCodePointCount, + codePointCount - matchedCodePointCount); } } // All characters are matched. if (codePointCount == readingHelper->getTotalCodePointCount()) { - if (nodeReader->isTerminal()) { - // TODO: Update probability. - } else { - // TODO: Make it terminal and update probability. - } - return false; + return setPtNodeProbability(nodeReader, probability, + readingHelper->getMergedNodeCodePoints()); } if (!nodeReader->hasChildren()) { - // TODO: Create children node array and add new node as a child. - return false; + return createChildrenPtNodeArrayAndAChildPtNode(nodeReader, probability, + wordCodePoints + readingHelper->getTotalCodePointCount(), + codePointCount - readingHelper->getTotalCodePointCount()); } // Advance to the children nodes. + parentPos = nodeReader->getNodePos(); readingHelper->readChildNode(); } if (readingHelper->isError()) { // The dictionary is invalid. return false; } - // TODO: add at the last position of the node array. - return false; + int pos = readingHelper->getPosOfLastForwardLinkField(); + return createAndInsertNodeIntoPtNodeArray(parentPos, + wordCodePoints + readingHelper->getPrevTotalCodePointCount(), + codePointCount - readingHelper->getPrevTotalCodePointCount(), + probability, &pos); } bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const int word1Pos, @@ -96,4 +99,243 @@ bool DynamicPatriciaTrieWritingHelper::removeBigramWords(const int word0Pos, con return false; } +bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition( + const DynamicPatriciaTrieNodeReader *const originalNode, const int movedPos) { + int pos = originalNode->getNodePos(); + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPatriciaTrieReadingUtils::updateAndGetFlags(originalFlags, true /* isMoved */, + false /* isDeleted */); + int writingPos = originalNode->getNodePos(); + // Update flags. + if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags, + &writingPos)) { + return false; + } + // Update moved position, which is stored in the parent offset field. + const int movedPosOffset = movedPos - originalNode->getNodePos(); + if (!DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition( + mBuffer, movedPosOffset, &writingPos)) { + return false; + } + return true; +} + +// Write new PtNode at writingPos. +bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer(const bool isBlacklisted, + const bool isNotAWord, const int parentPos, const int *const codePoints, + const int codePointCount, const int probability, const int childrenPos, + const int originalBigramListPos, const int originalShortcutListPos, + int *const writingPos) { + const int nodePos = *writingPos; + // Create node flags and write them. + const PatriciaTrieReadingUtils::NodeFlags nodeFlags = + PatriciaTrieReadingUtils::createAndGetFlags(isBlacklisted, isNotAWord, + probability != NOT_A_PROBABILITY, originalShortcutListPos != NOT_A_DICT_POS, + originalBigramListPos != NOT_A_DICT_POS, codePointCount > 1, + 3 /* childrenPositionFieldSize */); + if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, nodeFlags, + writingPos)) { + return false; + } + // Calculate a parent offset and write the offset. + const int parentOffset = (parentPos != NOT_A_DICT_POS) ? parentPos - nodePos : NOT_A_DICT_POS; + if (!DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition(mBuffer, + parentOffset, writingPos)) { + return false; + } + // Write code points + if (!DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(mBuffer, codePoints, + codePointCount, writingPos)) { + return false;; + } + // Write probability when the probability is a valid probability, which means this node is + // terminal. + if (probability != NOT_A_PROBABILITY) { + if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(mBuffer, + probability, writingPos)) { + return false; + } + } + // Write children position + if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, + childrenPos, writingPos)) { + return false; + } + // Copy shortcut list when the originalShortcutListPos is valid dictionary position. + if (originalShortcutListPos != NOT_A_DICT_POS) { + int fromPos = originalShortcutListPos; + if (!mShortcutPolicy->copyAllShortcutsAndReturnIfSucceededOrNot(&fromPos, writingPos)) { + return false; + } + } + // Copy bigram list when the originalBigramListPos is valid dictionary position. + if (originalBigramListPos != NOT_A_DICT_POS) { + int fromPos = originalBigramListPos; + if (!mBigramPolicy->copyAllBigrams(&fromPos, writingPos)) { + return false; + } + } + return true; +} + +bool DynamicPatriciaTrieWritingHelper::writePtNodeToBuffer(const int parentPos, + const int *const codePoints, const int codePointCount, const int probability, + int *const writingPos) { + return writePtNodeWithFullInfoToBuffer(false /* isBlacklisted */, false /* isNotAWord */, + parentPos, codePoints, codePointCount, probability, + NOT_A_DICT_POS /* childrenPos */, NOT_A_DICT_POS /* originalBigramsPos */, + NOT_A_DICT_POS /* originalShortcutPos */, writingPos); +} + +bool DynamicPatriciaTrieWritingHelper::writePtNodeToBufferByCopyingPtNodeInfo( + const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos, + const int *const codePoints, const int codePointCount, const int probability, + int *const writingPos) { + return writePtNodeWithFullInfoToBuffer(originalNode->isBlacklisted(), + originalNode->isNotAWord(), parentPos, codePoints, codePointCount, probability, + originalNode->getChildrenPos(), originalNode->getBigramsPos(), + originalNode->getShortcutPos(), writingPos); +} + +bool DynamicPatriciaTrieWritingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos, + const int *const nodeCodePoints, const int nodeCodePointCount, const int probability, + int *const forwardLinkFieldPos) { + const int newPtNodeArrayPos = mBuffer->getTailPosition(); + if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, + newPtNodeArrayPos, forwardLinkFieldPos)) { + return false; + } + return createNewPtNodeArrayWithAChildPtNode(parentPos, nodeCodePoints, nodeCodePointCount, + probability); +} + +bool DynamicPatriciaTrieWritingHelper::setPtNodeProbability( + const DynamicPatriciaTrieNodeReader *const originalPtNode, const int probability, + const int *const codePoints) { + if (originalPtNode->isTerminal()) { + // Overwrites the probability. + int probabilityFieldPos = originalPtNode->getProbabilityFieldPos(); + if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(mBuffer, + probability, &probabilityFieldPos)) { + return false; + } + } else { + // Make the node terminal and write the probability. + int movedPos = mBuffer->getTailPosition(); + if (!markNodeAsMovedAndSetPosition(originalPtNode, movedPos)) { + return false; + } + if (!writePtNodeToBufferByCopyingPtNodeInfo(originalPtNode, originalPtNode->getParentPos(), + codePoints, originalPtNode->getCodePointCount(), probability, &movedPos)) { + return false; + } + } + return true; +} + +bool DynamicPatriciaTrieWritingHelper::createChildrenPtNodeArrayAndAChildPtNode( + const DynamicPatriciaTrieNodeReader *const parentNode, const int probability, + const int *const codePoints, const int codePointCount) { + const int newPtNodeArrayPos = mBuffer->getTailPosition(); + int childrenPosFieldPos = parentNode->getChildrenPosFieldPos(); + if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, + newPtNodeArrayPos, &childrenPosFieldPos)) { + return false; + } + return createNewPtNodeArrayWithAChildPtNode(parentNode->getNodePos(), codePoints, + codePointCount, probability); +} + +bool DynamicPatriciaTrieWritingHelper::createNewPtNodeArrayWithAChildPtNode( + const int parentPtNodePos, const int *const nodeCodePoints, const int nodeCodePointCount, + const int probability) { + int writingPos = mBuffer->getTailPosition(); + if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, + 1 /* arraySize */, &writingPos)) { + return false; + } + if (!writePtNodeToBuffer(parentPtNodePos, nodeCodePoints, nodeCodePointCount, probability, + &writingPos)) { + return false; + } + if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, + NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { + return false; + } + return true; +} + +// Returns whether the dictionary updating was succeeded or not. +bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes( + const DynamicPatriciaTrieNodeReader *const reallocatingPtNode, + const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount, + const int probabilityOfNewPtNode, const int *const newNodeCodePoints, + const int newNodeCodePointCount) { + // When addsExtraChild is true, split the reallocating PtNode and add new child. + // Reallocating PtNode: abcde, newNode: abcxy. + // abc (1st, not terminal) __ de (2nd) + // \_ xy (extra child, terminal) + // Otherwise, this method makes 1st part terminal and write probabilityOfNewPtNode. + // Reallocating PtNode: abcde, newNode: abc. + // abc (1st, terminal) __ de (2nd) + const bool addsExtraChild = newNodeCodePointCount > overlappingCodePointCount; + const int firstPtNodePos = mBuffer->getTailPosition(); + if (!markNodeAsMovedAndSetPosition(reallocatingPtNode, firstPtNodePos)) { + return false; + } + int writingPos = firstPtNodePos; + // Write the 1st part of the reallocating node. The children position will be updated later + // with actual children position. + const int newProbability = addsExtraChild ? NOT_A_PROBABILITY : probabilityOfNewPtNode; + if (!writePtNodeToBuffer(reallocatingPtNode->getParentPos(), reallocatingPtNodeCodePoints, + overlappingCodePointCount, newProbability, &writingPos)) { + return false; + } + const int actualChildrenPos = writingPos; + // Create new children PtNode array. + const size_t newPtNodeCount = addsExtraChild ? 2 : 1; + if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, + newPtNodeCount, &writingPos)) { + return false; + } + // Write the 2nd part of the reallocating node. + if (!writePtNodeToBufferByCopyingPtNodeInfo(reallocatingPtNode, + reallocatingPtNode->getNodePos(), + reallocatingPtNodeCodePoints + overlappingCodePointCount, + reallocatingPtNode->getCodePointCount() - overlappingCodePointCount, + reallocatingPtNode->getProbability(), &writingPos)) { + return false; + } + if (addsExtraChild) { + if (!writePtNodeToBuffer(reallocatingPtNode->getNodePos(), + newNodeCodePoints + overlappingCodePointCount, + newNodeCodePointCount - overlappingCodePointCount, probabilityOfNewPtNode, + &writingPos)) { + return false; + } + } + if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, + NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { + return false; + } + // Load node info. Information of the 1st part will be fetched. + DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); + nodeReader.fetchNodeInfoFromBuffer(firstPtNodePos); + // Update children position. + int childrenPosFieldPos = nodeReader.getChildrenPosFieldPos(); + if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, + actualChildrenPos, &childrenPosFieldPos)) { + return false; + } + return true; +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h index f8165fc3d..ada634a54 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h @@ -23,6 +23,7 @@ namespace latinime { class BufferWithExtendableBuffer; class DynamicBigramListPolicy; +class DynamicPatriciaTrieNodeReader; class DynamicPatriciaTrieReadingHelper; class DynamicShortcutListPolicy; @@ -51,6 +52,41 @@ class DynamicPatriciaTrieWritingHelper { BufferWithExtendableBuffer *const mBuffer; DynamicBigramListPolicy *const mBigramPolicy; DynamicShortcutListPolicy *const mShortcutPolicy; + + bool markNodeAsMovedAndSetPosition(const DynamicPatriciaTrieNodeReader *const nodeToUpdate, + const int movedPos); + + bool writePtNodeWithFullInfoToBuffer(const bool isBlacklisted, const bool isNotAWord, + const int parentPos, const int *const codePoints, const int codePointCount, + const int probability, const int childrenPos, const int originalBigramListPos, + const int originalShortcutListPos, int *const writingPos); + + bool writePtNodeToBuffer(const int parentPos, const int *const codePoints, + const int codePointCount, const int probability, int *const writingPos); + + bool writePtNodeToBufferByCopyingPtNodeInfo( + const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos, + const int *const codePoints, const int codePointCount, const int probability, + int *const writingPos); + + bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints, + const int nodeCodePointCount, const int probability, int *const forwardLinkFieldPos); + + bool setPtNodeProbability(const DynamicPatriciaTrieNodeReader *const originalNode, + const int probability, const int *const codePoints); + + bool createChildrenPtNodeArrayAndAChildPtNode( + const DynamicPatriciaTrieNodeReader *const parentNode, const int probability, + const int *const codePoints, const int codePointCount); + + bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, const int *const nodeCodePoints, + const int nodeCodePointCount, const int probability); + + bool reallocatePtNodeAndAddNewPtNodes( + const DynamicPatriciaTrieNodeReader *const reallocatingPtNode, + const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount, + const int probabilityOfNewPtNode, const int *const newNodeCodePoints, + const int newNodeCodePointCount); }; } // namespace latinime #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp new file mode 100644 index 000000000..b261e594d --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" + +#include <cstddef> +#include <cstdlib> +#include <stdint.h> + +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD = 0x7F; +const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE = 0x7FFF; +const int DynamicPatriciaTrieWritingUtils::SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE = 1; +const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE = 2; +const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000; +const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_FIELD_SIZE = 3; +const int DynamicPatriciaTrieWritingUtils::MAX_DICT_OFFSET_VALUE = 0x7FFFFF; +const int DynamicPatriciaTrieWritingUtils::MIN_DICT_OFFSET_VALUE = -0x7FFFFF; +const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_NEGATIVE_FLAG = 0x800000; +const int DynamicPatriciaTrieWritingUtils::PROBABILITY_FIELD_SIZE = 1; +const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, + int *const forwardLinkFieldPos) { + const int offset = (forwardLinkPos != NOT_A_DICT_POS) ? + forwardLinkPos - (*forwardLinkFieldPos) : 0; + return writeDictOffset(buffer, offset, forwardLinkFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const size_t arraySize, + int *const arraySizeFieldPos) { + if (arraySize <= MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD) { + return buffer->writeUintAndAdvancePosition(arraySize, SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE, + arraySizeFieldPos); + } else if (arraySize <= MAX_PTNODE_ARRAY_SIZE) { + uint32_t data = arraySize | LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG; + return buffer->writeUintAndAdvancePosition(data, LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE, + arraySizeFieldPos); + } else { + AKLOGI("PtNode array size cannot be written because arraySize is too large: %zd", + arraySize); + ASSERT(false); + return false; + } +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, + const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos) { + return buffer->writeUintAndAdvancePosition(nodeFlags, NODE_FLAG_FIELD_SIZE, nodeFlagsFieldPos); +} + +// Note that parentOffset is offset from node's head position. +/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int parentOffset, + int *const parentPosFieldPos) { + int offset = (parentOffset != NOT_A_DICT_POS) ? parentOffset : 0; + return writeDictOffset(buffer, offset, parentPosFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int *const codePoints, + const int codePointCount, int *const codePointFieldPos) { + if (codePointCount <= 0) { + AKLOGI("code points cannot be written because codePointCount is invalid: %d", + codePointCount); + ASSERT(false); + return false; + } + const bool hasMultipleCodePoints = codePointCount > 1; + return buffer->writeCodePointsAndAdvancePosition(codePoints, codePointCount, + hasMultipleCodePoints, codePointFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int probability, + int *const probabilityFieldPos) { + if (probability < 0 || probability > MAX_PROBABILITY) { + AKLOGI("probability cannot be written because the probability is invalid: %d", + probability); + ASSERT(false); + return false; + } + return buffer->writeUintAndAdvancePosition(probability, PROBABILITY_FIELD_SIZE, + probabilityFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int childrenPosition, + int *const childrenPositionFieldPos) { + int offset = (childrenPosition != NOT_A_DICT_POS) ? + childrenPosition - (*childrenPositionFieldPos) : 0; + return writeDictOffset(buffer, offset, childrenPositionFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeDictOffset( + BufferWithExtendableBuffer *const buffer, const int offset, int *const offsetFieldPos) { + if (offset > MAX_DICT_OFFSET_VALUE || offset < MIN_DICT_OFFSET_VALUE) { + AKLOGI("offset cannot be written because the offset is too large or too small: %d", + offset); + ASSERT(false); + return false; + } + uint32_t data = 0; + if (offset >= 0) { + data = offset; + } else { + data = abs(offset) | DICT_OFFSET_NEGATIVE_FLAG; + } + return buffer->writeUintAndAdvancePosition(data, DICT_OFFSET_FIELD_SIZE, offsetFieldPos); +} +} diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h new file mode 100644 index 000000000..183ede444 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H +#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H + +#include <cstddef> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" + +namespace latinime { + +class BufferWithExtendableBuffer; + +class DynamicPatriciaTrieWritingUtils { + public: + static bool writeForwardLinkPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, + int *const forwardLinkFieldPos); + + static bool writePtNodeArraySizeAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const size_t arraySize, int *const arraySizeFieldPos); + + static bool writeFlagsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, + int *const nodeFlagsFieldPos); + + static bool writeParentOffsetAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int parentPosition, int *const parentPosFieldPos); + + static bool writeCodePointsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int *const codePoints, const int codePointCount, int *const codePointFieldPos); + + static bool writeProbabilityAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int probability, int *const probabilityFieldPos); + + static bool writeChildrenPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int childrenPosition, int *const childrenPositionFieldPos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingUtils); + + static const size_t MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD; + static const size_t MAX_PTNODE_ARRAY_SIZE; + static const int SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE; + static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE; + static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG; + static const int DICT_OFFSET_FIELD_SIZE; + static const int MAX_DICT_OFFSET_VALUE; + static const int MIN_DICT_OFFSET_VALUE; + static const int DICT_OFFSET_NEGATIVE_FLAG; + static const int NODE_FLAG_FIELD_SIZE; + static const int PROBABILITY_FIELD_SIZE; + + static bool writeDictOffset(BufferWithExtendableBuffer *const buffer, const int offset, + int *const offsetFieldPos); +}; +} // namespace latinime +#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp index eb828b58c..196da5c97 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp @@ -16,24 +16,115 @@ #include "suggest/policyimpl/dictionary/header/header_policy.h" +#include <cstddef> + namespace latinime { -const char *const HeaderPolicy::MULTIPLE_WORDS_DEMOTION_RATE_KEY = - "MULTIPLE_WORDS_DEMOTION_RATE"; -const float HeaderPolicy::DEFAULT_MULTI_WORD_COST_MULTIPLIER = 1.0f; -const float HeaderPolicy::MULTI_WORD_COST_MULTIPLIER_SCALE = 100.0f; +const char *const HeaderPolicy::MULTIPLE_WORDS_DEMOTION_RATE_KEY = "MULTIPLE_WORDS_DEMOTION_RATE"; +const char *const HeaderPolicy::USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE"; +const char *const HeaderPolicy::LAST_UPDATED_TIME_KEY = "date"; +const float HeaderPolicy::DEFAULT_MULTIPLE_WORD_COST_MULTIPLIER = 1.0f; +const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f; + +// Used for logging. Question mark is used to indicate that the key is not found. +void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *outValue, + int outValueSize) const { + if (outValueSize <= 0) return; + if (outValueSize == 1) { + outValue[0] = '\0'; + return; + } + std::vector<int> keyCodePointVector; + insertCharactersIntoVector(key, &keyCodePointVector); + HeaderReadingUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyCodePointVector); + if (it == mAttributeMap.end()) { + // The key was not found. + outValue[0] = '?'; + outValue[1] = '\0'; + return; + } + const int terminalIndex = min(static_cast<int>(it->second.size()), outValueSize - 1); + for (int i = 0; i < terminalIndex; ++i) { + outValue[i] = it->second[i]; + } + outValue[terminalIndex] = '\0'; +} + +float HeaderPolicy::readMultipleWordCostMultiplier() const { + int attributeValue = 0; + if (getAttributeValueAsInt(MULTIPLE_WORDS_DEMOTION_RATE_KEY, &attributeValue)) { + if (attributeValue <= 0) { + return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); + } + return MULTIPLE_WORD_COST_MULTIPLIER_SCALE / static_cast<float>(attributeValue); + } else { + return DEFAULT_MULTIPLE_WORD_COST_MULTIPLIER; + } +} + +bool HeaderPolicy::readUsesForgettingCurveFlag() const { + int attributeValue = 0; + if (getAttributeValueAsInt(USES_FORGETTING_CURVE_KEY, &attributeValue)) { + return attributeValue != 0; + } else { + return false; + } +} + +// Returns S_INT_MIN when the key is not found or the value is invalid. +int HeaderPolicy::readLastUpdatedTime() const { + int attributeValue = 0; + if (getAttributeValueAsInt(LAST_UPDATED_TIME_KEY, &attributeValue)) { + return attributeValue; + } else { + return S_INT_MIN; + } +} -float HeaderPolicy::readMultiWordCostMultiplier() const { - const int headerValue = HeaderReadingUtils::readHeaderValueInt( - mDictBuf, MULTIPLE_WORDS_DEMOTION_RATE_KEY); - if (headerValue == S_INT_MIN) { - // not found - return DEFAULT_MULTI_WORD_COST_MULTIPLIER; +// Returns whether the key is found or not and stores the found value into outValue. +bool HeaderPolicy::getAttributeValueAsInt(const char *const key, int *const outValue) const { + std::vector<int> keyVector; + insertCharactersIntoVector(key, &keyVector); + HeaderReadingUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyVector); + if (it == mAttributeMap.end()) { + // The key was not found. + return false; } - if (headerValue <= 0) { - return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); + *outValue = parseIntAttributeValue(&(it->second)); + return true; +} + +/* static */ HeaderReadingUtils::AttributeMap HeaderPolicy::createAttributeMapAndReadAllAttributes( + const uint8_t *const dictBuf) { + HeaderReadingUtils::AttributeMap attributeMap; + HeaderReadingUtils::fetchAllHeaderAttributes(dictBuf, &attributeMap); + return attributeMap; +} + +/* static */ int HeaderPolicy::parseIntAttributeValue( + const std::vector<int> *const attributeValue) { + int value = 0; + bool isNegative = false; + for (size_t i = 0; i < attributeValue->size(); ++i) { + if (i == 0 && attributeValue->at(i) == '-') { + isNegative = true; + } else { + if (!isdigit(attributeValue->at(i))) { + // If not a number, return S_INT_MIN + return S_INT_MIN; + } + value *= 10; + value += attributeValue->at(i) - '0'; + } + } + return isNegative ? -value : value; +} + +/* static */ void HeaderPolicy::insertCharactersIntoVector(const char *const characters, + std::vector<int> *const vector) { + for (int i = 0; characters[i]; ++i) { + vector->push_back(characters[i]); } - return MULTI_WORD_COST_MULTIPLIER_SCALE / static_cast<float>(headerValue); } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h index e3e6fc077..930b475c7 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -17,6 +17,7 @@ #ifndef LATINIME_HEADER_POLICY_H #define LATINIME_HEADER_POLICY_H +#include <cctype> #include <stdint.h> #include "defines.h" @@ -30,7 +31,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { explicit HeaderPolicy(const uint8_t *const dictBuf) : mDictBuf(dictBuf), mDictionaryFlags(HeaderReadingUtils::getFlags(dictBuf)), mSize(HeaderReadingUtils::getHeaderSize(dictBuf)), - mMultiWordCostMultiplier(readMultiWordCostMultiplier()) {} + mAttributeMap(createAttributeMapAndReadAllAttributes(mDictBuf)), + mMultiWordCostMultiplier(readMultipleWordCostMultiplier()), + mUsesForgettingCurve(readUsesForgettingCurveFlag()), + mLastUpdatedTime(readLastUpdatedTime()) {} ~HeaderPolicy() {} @@ -55,34 +59,49 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { return mMultiWordCostMultiplier; } - AK_FORCE_INLINE void readHeaderValueOrQuestionMark(const char *const key, - int *outValue, int outValueSize) const { - if (outValueSize <= 0) return; - if (outValueSize == 1) { - outValue[0] = '\0'; - return; - } - if (!HeaderReadingUtils::readHeaderValue(mDictBuf, - key, outValue, outValueSize)) { - outValue[0] = '?'; - outValue[1] = '\0'; - } + AK_FORCE_INLINE bool usesForgettingCurve() const { + return mUsesForgettingCurve; } + AK_FORCE_INLINE int getLastUpdatedTime() const { + return mLastUpdatedTime; + } + + void readHeaderValueOrQuestionMark(const char *const key, + int *outValue, int outValueSize) const; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderPolicy); static const char *const MULTIPLE_WORDS_DEMOTION_RATE_KEY; - static const float DEFAULT_MULTI_WORD_COST_MULTIPLIER; - static const float MULTI_WORD_COST_MULTIPLIER_SCALE; + static const char *const USES_FORGETTING_CURVE_KEY; + static const char *const LAST_UPDATED_TIME_KEY; + static const float DEFAULT_MULTIPLE_WORD_COST_MULTIPLIER; + static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE; const uint8_t *const mDictBuf; const HeaderReadingUtils::DictionaryFlags mDictionaryFlags; const int mSize; + HeaderReadingUtils::AttributeMap mAttributeMap; const float mMultiWordCostMultiplier; + const bool mUsesForgettingCurve; + const int mLastUpdatedTime; - float readMultiWordCostMultiplier() const; -}; + float readMultipleWordCostMultiplier() const; + + bool readUsesForgettingCurveFlag() const; + int readLastUpdatedTime() const; + + bool getAttributeValueAsInt(const char *const key, int *const outValue) const; + + static HeaderReadingUtils::AttributeMap createAttributeMapAndReadAllAttributes( + const uint8_t *const dictBuf); + + static int parseIntAttributeValue(const std::vector<int> *const attributeValue); + + static void insertCharactersIntoVector( + const char *const characters, std::vector<int> *const vector); +}; } // namespace latinime #endif /* LATINIME_HEADER_POLICY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp index f323876c4..186c043c1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp @@ -16,23 +16,22 @@ #include "suggest/policyimpl/dictionary/header/header_reading_utils.h" -#include <cctype> -#include <cstdlib> +#include <vector> #include "defines.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" namespace latinime { -const int HeaderReadingUtils::MAX_OPTION_KEY_LENGTH = 256; +const int HeaderReadingUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256; +const int HeaderReadingUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256; const int HeaderReadingUtils::HEADER_MAGIC_NUMBER_SIZE = 4; const int HeaderReadingUtils::HEADER_DICTIONARY_VERSION_SIZE = 2; const int HeaderReadingUtils::HEADER_FLAG_SIZE = 2; const int HeaderReadingUtils::HEADER_SIZE_FIELD_SIZE = 4; -const HeaderReadingUtils::DictionaryFlags - HeaderReadingUtils::NO_FLAGS = 0; +const HeaderReadingUtils::DictionaryFlags HeaderReadingUtils::NO_FLAGS = 0; // Flags for special processing // Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or // something very bad (like, the apocalypse) will happen. Please update both at the same time. @@ -56,53 +55,27 @@ const HeaderReadingUtils::DictionaryFlags HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE); } -// Returns if the key is found or not and reads the found value into outValue. -/* static */ bool HeaderReadingUtils::readHeaderValue(const uint8_t *const dictBuf, - const char *const key, int *outValue, const int outValueSize) { - if (outValueSize <= 0) { - return false; - } +/* static */ void HeaderReadingUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf, + AttributeMap *const headerAttributes) { const int headerSize = getHeaderSize(dictBuf); int pos = getHeaderOptionsPosition(); if (pos == NOT_A_DICT_POS) { // The header doesn't have header options. - return false; + return; } + int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH]; + int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH]; while (pos < headerSize) { - if(ByteArrayUtils::compareStringInBufferWithCharArray( - dictBuf, key, headerSize - pos, &pos) == 0) { - // The key was found. - const int length = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, outValueSize, - outValue, &pos); - // Add a 0 terminator to the string. - outValue[length < outValueSize ? length : outValueSize - 1] = '\0'; - return true; - } - ByteArrayUtils::advancePositionToBehindString(dictBuf, headerSize - pos, &pos); - } - // The key was not found. - return false; -} - -/* static */ int HeaderReadingUtils::readHeaderValueInt( - const uint8_t *const dictBuf, const char *const key) { - const int bufferSize = LARGEST_INT_DIGIT_COUNT; - int intBuffer[bufferSize]; - char charBuffer[bufferSize]; - if (!readHeaderValue(dictBuf, key, intBuffer, bufferSize)) { - return S_INT_MIN; - } - for (int i = 0; i < bufferSize; ++i) { - charBuffer[i] = intBuffer[i]; - if (charBuffer[i] == '0') { - break; - } - if (!isdigit(charBuffer[i])) { - // If not a number, return S_INT_MIN - return S_INT_MIN; - } + const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, + MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos); + std::vector<int> key; + key.insert(key.end(), keyBuffer, keyBuffer + keyLength); + const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, + MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos); + std::vector<int> value; + value.insert(value.end(), valueBuffer, valueBuffer + valueLength); + headerAttributes->insert(AttributeMap::value_type(key, value)); } - return atoi(charBuffer); } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h index c94919640..5716198fb 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h @@ -17,7 +17,9 @@ #ifndef LATINIME_HEADER_READING_UTILS_H #define LATINIME_HEADER_READING_UTILS_H +#include <map> #include <stdint.h> +#include <vector> #include "defines.h" @@ -26,8 +28,7 @@ namespace latinime { class HeaderReadingUtils { public: typedef uint16_t DictionaryFlags; - - static const int MAX_OPTION_KEY_LENGTH; + typedef std::map<std::vector<int>, std::vector<int> > AttributeMap; static int getHeaderSize(const uint8_t *const dictBuf); @@ -50,14 +51,15 @@ class HeaderReadingUtils { + HEADER_SIZE_FIELD_SIZE; } - static bool readHeaderValue(const uint8_t *const dictBuf, - const char *const key, int *outValue, const int outValueSize); - - static int readHeaderValueInt(const uint8_t *const dictBuf, const char *const key); + static void fetchAllHeaderAttributes(const uint8_t *const dictBuf, + AttributeMap *const headerAttributes); private: DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadingUtils); + static const int MAX_ATTRIBUTE_KEY_LENGTH; + static const int MAX_ATTRIBUTE_VALUE_LENGTH; + static const int HEADER_MAGIC_NUMBER_SIZE; static const int HEADER_DICTIONARY_VERSION_SIZE; static const int HEADER_FLAG_SIZE; diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp index adcf2dbdf..d5a83a938 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp @@ -21,6 +21,7 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" namespace latinime { @@ -306,7 +307,19 @@ int PatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const inWord, } } -int PatriciaTriePolicy::getUnigramProbability(const int nodePos) const { +int PatriciaTriePolicy::getProbability(const int unigramProbability, + const int bigramProbability) const { + if (unigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else if (bigramProbability == NOT_A_PROBABILITY) { + return ProbabilityUtils::backoff(unigramProbability); + } else { + return ProbabilityUtils::computeProbabilityForBigram(unigramProbability, + bigramProbability); + } +} + +int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const { if (nodePos == NOT_A_VALID_WORD_POS) { return NOT_A_PROBABILITY; } @@ -324,7 +337,8 @@ int PatriciaTriePolicy::getUnigramProbability(const int nodePos) const { return NOT_A_PROBABILITY; } PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos); - return PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos); + return getProbability(PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition( + mDictRoot, &pos), NOT_A_PROBABILITY); } int PatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const { diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h index d0567fd85..75d976205 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h @@ -56,7 +56,9 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getTerminalNodePositionOfWord(const int *const inWord, const int length, const bool forceLowerCaseSearch) const; - int getUnigramProbability(const int nodePos) const; + int getProbability(const int unigramProbability, const int bigramProbability) const; + + int getUnigramProbabilityOfPtNode(const int nodePos) const; int getShortcutPositionOfNode(const int nodePos) const; diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h index f76c38751..2b0646db2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h @@ -119,6 +119,29 @@ class PatriciaTrieReadingUtils { return FLAG_CHILDREN_POSITION_TYPE_NOPOSITION != (MASK_CHILDREN_POSITION_TYPE & flags); } + static AK_FORCE_INLINE NodeFlags createAndGetFlags(const bool isBlacklisted, + const bool isNotAWord, const bool isTerminal, const bool hasShortcutTargets, + const bool hasBigrams, const bool hasMultipleChars, + const int childrenPositionFieldSize) { + NodeFlags nodeFlags = 0; + nodeFlags = isBlacklisted ? (nodeFlags | FLAG_IS_BLACKLISTED) : nodeFlags; + nodeFlags = isNotAWord ? (nodeFlags | FLAG_IS_NOT_A_WORD) : nodeFlags; + nodeFlags = isTerminal ? (nodeFlags | FLAG_IS_TERMINAL) : nodeFlags; + nodeFlags = hasShortcutTargets ? (nodeFlags | FLAG_HAS_SHORTCUT_TARGETS) : nodeFlags; + nodeFlags = hasBigrams ? (nodeFlags | FLAG_HAS_BIGRAMS) : nodeFlags; + nodeFlags = hasMultipleChars ? (nodeFlags | FLAG_HAS_MULTIPLE_CHARS) : nodeFlags; + if (childrenPositionFieldSize == 1) { + nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_ONEBYTE; + } else if (childrenPositionFieldSize == 2) { + nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_TWOBYTES; + } else if (childrenPositionFieldSize == 3) { + nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_THREEBYTES; + } else { + nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_NOPOSITION; + } + return nodeFlags; + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTrieReadingUtils); diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h index 5e9c52950..1803c09cb 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h @@ -83,8 +83,8 @@ class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { } // Copy shortcuts from the shortcut list that starts at fromPos to toPos and advance these - // positions after the shortcut lists. - void copyAllShortcuts(int *const fromPos, int *const toPos) { + // positions after the shortcut lists. This returns whether the copy was succeeded or not. + bool copyAllShortcutsAndReturnIfSucceededOrNot(int *const fromPos, int *const toPos) { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*fromPos); const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { @@ -93,16 +93,23 @@ class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { const int shortcutListSize = ShortcutListReadingUtils ::getShortcutListSizeAndForwardPointer(buffer, fromPos); // Copy shortcut list size. - mBuffer->writeUintAndAdvancePosition( + if (!mBuffer->writeUintAndAdvancePosition( shortcutListSize + ShortcutListReadingUtils::getShortcutListSizeFieldSize(), - ShortcutListReadingUtils::getShortcutListSizeFieldSize(), toPos); + ShortcutListReadingUtils::getShortcutListSizeFieldSize(), toPos)) { + return false; + } + // Copy shortcut list. for (int i = 0; i < shortcutListSize; ++i) { - const uint8_t data = ByteArrayUtils::readUint8AndAdvancePosition(buffer, fromPos); - mBuffer->writeUintAndAdvancePosition(data, 1 /* size */, toPos); + const uint8_t data = ByteArrayUtils::readUint8AndAdvancePosition( + mBuffer->getBuffer(usesAdditionalBuffer), fromPos); + if (!mBuffer->writeUintAndAdvancePosition(data, 1 /* size */, toPos)) { + return false; + } } if (usesAdditionalBuffer) { *fromPos += mBuffer->getOriginalBufferSize(); } + return true; } private: diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp index 8582c4b81..dfdaebd18 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp @@ -22,4 +22,71 @@ const size_t BufferWithExtendableBuffer::INITIAL_ADDITIONAL_BUFFER_SIZE = 16 * 1 const size_t BufferWithExtendableBuffer::MAX_ADDITIONAL_BUFFER_SIZE = 1024 * 1024; const size_t BufferWithExtendableBuffer::EXTEND_ADDITIONAL_BUFFER_SIZE_STEP = 16 * 1024; +bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data, const int size, + int *const pos) { + if (!(size >= 1 && size <= 4)) { + AKLOGI("writeUintAndAdvancePosition() is called with invalid size: %d", size); + ASSERT(false); + return false; + } + if (!checkAndPrepareWriting(*pos, size)) { + return false; + } + const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos); + uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer; + if (usesAdditionalBuffer) { + *pos -= mOriginalBufferSize; + } + ByteArrayUtils::writeUintAndAdvancePosition(buffer, data, size, pos); + if (usesAdditionalBuffer) { + *pos += mOriginalBufferSize; + } + return true; +} + +bool BufferWithExtendableBuffer::writeCodePointsAndAdvancePosition(const int *const codePoints, + const int codePointCount, const bool writesTerminator ,int *const pos) { + const size_t size = ByteArrayUtils::calculateRequiredByteCountToStoreCodePoints( + codePoints, codePointCount, writesTerminator); + if (!checkAndPrepareWriting(*pos, size)) { + return false; + } + const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos); + uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer; + if (usesAdditionalBuffer) { + *pos -= mOriginalBufferSize; + } + ByteArrayUtils::writeCodePointsAndAdvancePosition(buffer, codePoints, codePointCount, + writesTerminator, pos); + if (usesAdditionalBuffer) { + *pos += mOriginalBufferSize; + } + return true; +} + +bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int size) { + if (isInAdditionalBuffer(pos)) { + const int tailPosition = getTailPosition(); + if (pos == tailPosition) { + // Append data to the tail. + if (pos + size > static_cast<int>(mAdditionalBuffer.size()) + mOriginalBufferSize) { + // Need to extend buffer. + if (!extendBuffer()) { + return false; + } + } + mUsedAdditionalBufferSize += size; + } else if (pos + size >= tailPosition) { + // The access will beyond the tail of used region. + return false; + } + } else { + if (pos < 0 || mOriginalBufferSize < pos + size) { + // Invalid position or violate the boundary. + return false; + } + } + return true; +} + } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h index ec871ec85..c6a484131 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h @@ -47,6 +47,7 @@ class BufferWithExtendableBuffer { return position >= mOriginalBufferSize; } + // TODO: Resolve the issue that the address can be changed when the vector is resized. // CAVEAT!: Be careful about array out of bound access with buffers AK_FORCE_INLINE const uint8_t *getBuffer(const bool usesAdditionalBuffer) const { if (usesAdditionalBuffer) { @@ -66,27 +67,10 @@ class BufferWithExtendableBuffer { * Writing is allowed for original buffer, already written region of additional buffer and the * tail of additional buffer. */ - AK_FORCE_INLINE bool writeUintAndAdvancePosition(const uint32_t data, const int size, - int *const pos) { - if (!(size >= 1 && size <= 4)) { - AKLOGI("writeUintAndAdvancePosition() is called with invalid size: %d", size); - ASSERT(false); - return false; - } - if (!checkAndPrepareWriting(*pos, size)) { - return false; - } - const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos); - uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer; - if (usesAdditionalBuffer) { - *pos -= mOriginalBufferSize; - } - ByteArrayUtils::writeUintAndAdvancePosition(buffer, data, size, pos); - if (usesAdditionalBuffer) { - *pos += mOriginalBufferSize; - } - return true; - } + bool writeUintAndAdvancePosition(const uint32_t data, const int size, int *const pos); + + bool writeCodePointsAndAdvancePosition(const int *const codePoints, const int codePointCount, + const bool writesTerminator, int *const pos); private: DISALLOW_COPY_AND_ASSIGN(BufferWithExtendableBuffer); @@ -112,29 +96,7 @@ class BufferWithExtendableBuffer { // Returns if it is possible to write size-bytes from pos. When pos is at the tail position of // the additional buffer, try extending the buffer. - AK_FORCE_INLINE bool checkAndPrepareWriting(const int pos, const int size) { - if (isInAdditionalBuffer(pos)) { - if (pos == mUsedAdditionalBufferSize) { - // Append data to the tail. - if (pos + size > static_cast<int>(mAdditionalBuffer.size())) { - // Need to extend buffer. - if (!extendBuffer()) { - return false; - } - } - mUsedAdditionalBufferSize += size; - } else if (pos + size >= mUsedAdditionalBufferSize) { - // The access will beyond the tail of used region. - return false; - } - } else { - if (pos < 0 || mOriginalBufferSize < pos + size) { - // Invalid position or violate the boundary. - return false; - } - } - return true; - } + AK_FORCE_INLINE bool checkAndPrepareWriting(const int pos, const int size); }; } #endif /* LATINIME_BUFFER_WITH_EXTENDABLE_BUFFER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h index 1d14929c7..f727ecf8e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h @@ -115,7 +115,7 @@ class ByteArrayUtils { } /** - * Code Point + * Code Point Reading * * 1 byte = bbbbbbbb match * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte @@ -149,7 +149,7 @@ class ByteArrayUtils { } /** - * String (array of code points) + * String (array of code points) Reading * * Reads code points until the terminator is found. */ @@ -176,37 +176,49 @@ class ByteArrayUtils { return length; } - // Returns an integer less than, equal to, or greater than zero when string starting from pos - // in buffer is less than, match, or is greater than charArray. - static AK_FORCE_INLINE int compareStringInBufferWithCharArray(const uint8_t *const buffer, - const char *const charArray, const int maxLength, int *const pos) { - int index = 0; - int codePoint = readCodePointAndAdvancePosition(buffer, pos); - const uint8_t *const uint8CharArrayForComparison = - reinterpret_cast<const uint8_t *>(charArray); - while (NOT_A_CODE_POINT != codePoint - && '\0' != uint8CharArrayForComparison[index] && index < maxLength) { - if (codePoint != uint8CharArrayForComparison[index]) { - // Different character is found. - // Skip the rest of the string in the buffer. - advancePositionToBehindString(buffer, maxLength - index, pos); - return codePoint - uint8CharArrayForComparison[index]; + /** + * String (array of code points) Writing + */ + static void writeCodePointsAndAdvancePosition(uint8_t *const buffer, + const int *const codePoints, const int codePointCount, const bool writesTerminator, + int *const pos) { + for (int i = 0; i < codePointCount; ++i) { + const int codePoint = codePoints[i]; + if (codePoint == NOT_A_CODE_POINT || codePoint == CHARACTER_ARRAY_TERMINATOR) { + break; + } else if (codePoint < MINIMAL_ONE_BYTE_CHARACTER_VALUE) { + // three bytes character. + writeUint24AndAdvancePosition(buffer, codePoint, pos); + } else { + // one byte character. + writeUint8AndAdvancePosition(buffer, codePoint, pos); } - // Advance - codePoint = readCodePointAndAdvancePosition(buffer, pos); - ++index; } - if (NOT_A_CODE_POINT != codePoint && index < maxLength) { - // Skip the rest of the string in the buffer. - advancePositionToBehindString(buffer, maxLength - index, pos); + if (writesTerminator) { + writeUint8AndAdvancePosition(buffer, CHARACTER_ARRAY_TERMINATOR, pos); } - if (NOT_A_CODE_POINT == codePoint && '\0' == uint8CharArrayForComparison[index]) { - // When both of the last characters are terminals, we consider the string in the buffer - // matches the given char array - return 0; - } else { - return codePoint - uint8CharArrayForComparison[index]; + } + + static int calculateRequiredByteCountToStoreCodePoints(const int *const codePoints, + const int codePointCount, const bool writesTerminator) { + int byteCount = 0; + for (int i = 0; i < codePointCount; ++i) { + const int codePoint = codePoints[i]; + if (codePoint == NOT_A_CODE_POINT || codePoint == CHARACTER_ARRAY_TERMINATOR) { + break; + } else if (codePoint < MINIMAL_ONE_BYTE_CHARACTER_VALUE) { + // three bytes character. + byteCount += 3; + } else { + // one byte character. + byteCount += 1; + } + } + if (writesTerminator) { + // The terminator is one byte. + byteCount += 1; } + return byteCount; } private: diff --git a/native/jni/src/suggest/core/dictionary/probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h index 21fe355b8..21fe355b8 100644 --- a/native/jni/src/suggest/core/dictionary/probability_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java index be1ecf787..9ad81c01d 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java @@ -30,9 +30,9 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED); // Press "?123" key and hold, enter into choring symbols state. - pressKey(CODE_SYMBOL, SYMBOLS); + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); // Press/release symbol letter key. - chordingPressAndReleaseKey('1', SYMBOLS, SYMBOLS); + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Release "ABC" key, switch back to alphabet. releaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); } @@ -50,9 +50,9 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { releaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED); // Press "?123" key and hold, enter into choring symbols state. - pressKey(CODE_SYMBOL, SYMBOLS); + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); // Press/release symbol letter key. - chordingPressAndReleaseKey('1', SYMBOLS, SYMBOLS); + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Release "123?" key, switch back to alphabet unshifted. releaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); } @@ -71,9 +71,9 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED); // Press "?123" key and hold, enter into choring symbols state. - pressKey(CODE_SYMBOL, SYMBOLS); + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); // Press/release symbol letter key. - chordingPressAndReleaseKey('1', SYMBOLS, SYMBOLS); + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Release "123?" key, switch back to alphabet shift locked. releaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED); } @@ -81,14 +81,21 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Chording input in symbols. public void testChordingSymbols() { // Press/release "?123" key, enter symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press "=\<" key and hold, enter into choring symbols shifted state. + pressKey(CODE_SHIFT, SYMBOLS_SHIFTED); + // Press/release symbol letter key. + chordingPressAndReleaseKey('1', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Release "=\<" key, switch back to symbols. + releaseKey(CODE_SHIFT, SYMBOLS_UNSHIFTED); // Press "ABC" key and hold, enter into choring alphabet state. pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); // Press/release letter key. chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Release "ABC" key, switch back to symbols. - releaseKey(CODE_SYMBOL, SYMBOLS); + releaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); // Alphabet shifted -> symbols -> "ABC" key + letter -> symbols // -> alphabet. @@ -97,13 +104,13 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press "ABC" key, enter into chording alphabet state. pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); // Enter/release letter key. chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Release "ABC" key, switch back to symbols. - releaseKey(CODE_SYMBOL, SYMBOLS); + releaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); // Press/release "ABC" key, switch to alphabet (not alphabet shifted). pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); @@ -115,13 +122,112 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press "ABC" key, enter into chording alphabet shift locked. pressKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED); // Enter/release letter key. chordingPressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); // Release "ABC" key, switch back to symbols. - releaseKey(CODE_SYMBOL, SYMBOLS); + releaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols -> "=\<" key + letter -> symbols -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press "=\<" key, enter into symbols shifted chording state. + pressKey(CODE_SHIFT, SYMBOLS_SHIFTED); + // Enter/release symbols shift letter key. + chordingPressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Release "=\<" key, switch back to symbols. + releaseKey(CODE_SHIFT, SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + } + + // Chording input in symbol shifted. + public void testChordingSymbolsShifted() { + // Press/release "?123" key, enter symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Press "?123" key and hold, enter into chording symbols state. + pressKey(CODE_SHIFT, SYMBOLS_UNSHIFTED); + // Press/release symbol letter key. + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Release "=\<" key, switch back to symbols shifted state. + releaseKey(CODE_SHIFT, SYMBOLS_SHIFTED); + + // Press "ABC" key and hold, enter into choring alphabet state. + pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); + // Press/release letter key. + chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Release "ABC" key, switch back to symbols. + releaseKey(CODE_SYMBOL, SYMBOLS_SHIFTED); + + // Alphabet shifted -> symbols shifted -> "ABC" key + letter -> symbols shifted -> + // alphabet. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Press/release shift key, enter alphabet shifted. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press "ABC" key, enter into chording alphabet state. + pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); + // Enter/release letter key. + chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Release "ABC" key, switch back to symbols shifted. + releaseKey(CODE_SYMBOL, SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet (not alphabet shifted). + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Alphabet shift locked -> symbols shifted -> "ABC" key + letter -> symbols shifted + // -> alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press "ABC" key, enter into chording alphabet shift locked. + pressKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED); + // Enter/release letter key. + chordingPressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + // Release "ABC" key, switch back to symbols shifted. + releaseKey(CODE_SYMBOL, SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols shifted -> "=\<" key + letter -> symbols shifted + // -> alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press "=\<" key, enter into symbols chording state. + pressKey(CODE_SHIFT, SYMBOLS_UNSHIFTED); + // Enter/release symbols letter key. + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Release "=\<" key, switch back to symbols shifted. + releaseKey(CODE_SHIFT, SYMBOLS_SHIFTED); // Press/release "ABC" key, switch to alphabet shift locked. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); } @@ -143,9 +249,9 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Update shift state with auto caps enabled. pressAndReleaseKey(CODE_AUTO_CAPS_TRIGGER, ALPHABET_UNSHIFTED, ALPHABET_AUTOMATIC_SHIFTED); // Press "123?" key and hold, enter into chording symbols state. - pressKey(CODE_SYMBOL, SYMBOLS); + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); // Press/release symbol letter key. - chordingPressAndReleaseKey('1', SYMBOLS, SYMBOLS); + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Release "123?" key, switch back to alphabet. releaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java index 611482bcf..c7ac76d93 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java @@ -34,14 +34,38 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); } + // Shift key in symbols. + public void testShiftSymbols() { + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Press/release "?123" key, back to symbols. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press/release symbol letter key, remain in symbols shifted. + pressAndReleaseKey('1', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + } + // Switching between alphabet and symbols. public void testAlphabetAndSymbols() { // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press/release "ABC" key, back to alphabet. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, back to alphabet. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Press/release "?123" key, back to symbols (not symbols shifted). + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); } // Switching between alphabet shifted and symbols. @@ -50,7 +74,16 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, back to alphabet (not alphabet shifted). + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Press/release shift key, enter into alphabet shifted. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\< key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); // Press/release "ABC" key, back to alphabet (not alphabet shifted). pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); } @@ -62,21 +95,39 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press/release "ABC" key, back to alphabet shift locked. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, back to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, back to symbols (not symbols shifted). + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); } // Automatic switch back to alphabet by space key. public void testSwitchBackBySpace() { // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter symbol letter. - pressAndReleaseKey('1', SYMBOLS, SYMBOLS); + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Enter space, switch back to alphabet. + pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter symbol shift letter. + pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); // Enter space, switch back to alphabet. - pressAndReleaseKey(CODE_SPACE, SYMBOLS, ALPHABET_UNSHIFTED); + pressAndReleaseKey(CODE_SPACE, SYMBOLS_SHIFTED, ALPHABET_UNSHIFTED); + // Press/release "?123" key, enter into symbols (not symbols shifted). + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); } // Automatic switch back to alphabet shift locked test by space key. @@ -86,11 +137,20 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter symbol letter. - pressAndReleaseKey('1', SYMBOLS, SYMBOLS); + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter space, switch back to alphabet shift locked. - pressAndReleaseKey(CODE_SPACE, SYMBOLS, ALPHABET_SHIFT_LOCKED); + pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter symbol shift letter. + pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter space, switch back to alphabet shift locked. + pressAndReleaseKey(CODE_SPACE, SYMBOLS_SHIFTED, ALPHABET_SHIFT_LOCKED); } // Automatic upper case test @@ -113,11 +173,20 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { pressAndReleaseKey(CODE_AUTO_CAPS_TRIGGER, ALPHABET_UNSHIFTED, ALPHABET_AUTOMATIC_SHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press/release symbol letter key, remain in symbols. - pressAndReleaseKey('1', SYMBOLS, SYMBOLS); + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release space, switch back to automatic shifted. + pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press/release symbol shift letter key, remain in symbols shifted. + pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); // Press/release space, switch back to automatic shifted. - pressAndReleaseKey(CODE_SPACE, SYMBOLS, ALPHABET_AUTOMATIC_SHIFTED); + pressAndReleaseKey(CODE_SPACE, SYMBOLS_SHIFTED, ALPHABET_AUTOMATIC_SHIFTED); } // Long press shift key. @@ -233,9 +302,14 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { ALPHABET_UNSHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Update shift state, remained in symbols. - updateShiftState(SYMBOLS); + updateShiftState(SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Update shift state, remained in symbols shifted. + updateShiftState(SYMBOLS_SHIFTED); // Set capitalize the first character of all words mode. setAutoCapsMode(CAP_MODE_WORDS); @@ -263,9 +337,14 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in automatic shifted. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Update shift state, remained in symbols. - updateShiftState(SYMBOLS); + updateShiftState(SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Update shift state, remained in symbols shifted. + updateShiftState(SYMBOLS_SHIFTED); } // Sliding input in alphabet. @@ -279,10 +358,10 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet -> "?123" key + letter -> alphabet. // Press and slide from "123?" key, enter symbols. - pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter/release into symbol letter keys, switch back to alphabet. - pressAndSlideFromKey('@', SYMBOLS, SYMBOLS); - stopSlidingOnKey('!', SYMBOLS, ALPHABET_UNSHIFTED); + pressAndSlideFromKey('@', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + stopSlidingOnKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); // Alphabet shifted -> shift key + letter -> alphabet. // Press/release shift key, enter alphabet shifted. @@ -297,20 +376,20 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press and slide from "123?" key, enter symbols. - pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter/release into symbol letter keys, switch back to alphabet (not alphabet shifted). - pressAndSlideFromKey('@', SYMBOLS, SYMBOLS); - stopSlidingOnKey('!', SYMBOLS, ALPHABET_UNSHIFTED); + pressAndSlideFromKey('@', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + stopSlidingOnKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); // Alphabet shift locked -> shift key + letter -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press and slide from "123?" key, enter symbols. - pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter/release into symbol letter keys, switch back to alphabet shift locked. - pressAndSlideFromKey('!', SYMBOLS, SYMBOLS); - stopSlidingOnKey('!', SYMBOLS, ALPHABET_SHIFT_LOCKED); + pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + stopSlidingOnKey('!', SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED); // Alphabet shift locked -> "?123" key + letter -> alphabet shift locked. // Press and slide from shift key, enter alphabet shifted. @@ -334,9 +413,9 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet -> "?123" key + letter -> cancel -> alphabet. // Press and slide from "123?" key, enter symbols. - pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter/release into symbol letter key, remains in symbols. - pressAndSlideFromKey('!', SYMBOLS, SYMBOLS); + pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Cancel sliding, switch back to alphabet. stopSlidingAndCancel(ALPHABET_UNSHIFTED); @@ -354,9 +433,9 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press and slide from "123?" key, enter symbols. - pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter/release into symbol letter key, remains in symbols. - pressAndSlideFromKey('!', SYMBOLS, SYMBOLS); + pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Cancel sliding, switch back to alphabet (not alphabet shifted). stopSlidingAndCancel(ALPHABET_UNSHIFTED); @@ -365,9 +444,9 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press and slide from "123?" key, enter symbols. - pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Enter/release into symbol letter key, remains in symbols. - pressAndSlideFromKey('!', SYMBOLS, SYMBOLS); + pressAndSlideFromKey('!', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Cancel sliding, switch back to alphabet shift locked. stopSlidingAndCancel( ALPHABET_SHIFT_LOCKED); @@ -382,14 +461,21 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Sliding input in symbols. public void testSlidingSymbols() { + // Symbols -> "=\<" key + letter -> symbols. // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press and slide from shift key, enter symbols shifted. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter/release symbol shifted letter keys, switch back to symbols. + pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + stopSlidingOnKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED); + // Symbols -> "ABC" key + letter -> Symbols. // Press and slide from "ABC" key, enter alphabet. pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Enter/release letter keys, switch back to symbols. pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); - stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS); + stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press/release "ABC" key, switch to alphabet. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); @@ -400,12 +486,12 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press and slide from "ABC" key. pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Enter/release letter keys, switch back to symbols. pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); - stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS); + stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press/release "ABC" key, switch to alphabet (not alphabet shifted). pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); @@ -417,27 +503,52 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press and slide from "ABC" key, enter alphabet shift locked. pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); // Enter/release letter keys, switch back to symbols. pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); - stopSlidingOnKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS); + stopSlidingOnKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols -> "=\<" key + letter -> symbols -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press and slide from "=\<" key, enter symbols shifted. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter/release symbols shift letter keys, switch back to symbols. + pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + stopSlidingOnKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED); // Press/release "ABC" key, switch to alphabet shift locked. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); } // Cancel sliding input in symbols. public void testSlidingSymbolsCancel() { + // Symbols -> "=\<" key + letter -> cancel -> symbols. // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press and slide from shift key, enter symbols shifted. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter/release symbol shifted letter key, remains in symbols shifted. + pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Cancel sliding, switch back to symbols. + stopSlidingAndCancel(SYMBOLS_UNSHIFTED); + // Symbols -> "ABC" key + letter -> Symbols. // Press and slide from "ABC" key, enter alphabet. pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Enter/release letter keys, remains in alphabet. pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Cancel sliding, switch back to symbols. - stopSlidingAndCancel(SYMBOLS); + stopSlidingAndCancel(SYMBOLS_UNSHIFTED); // Press/release "ABC" key, switch to alphabet. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); @@ -448,13 +559,13 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press and slide from "ABC" key. pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Enter/release letter key, remains in alphabet. pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); // Cancel sliding, switch back to symbols. - stopSlidingAndCancel(SYMBOLS); + stopSlidingAndCancel(SYMBOLS_UNSHIFTED); // Press/release "ABC" key, switch to alphabet (not alphabet shifted). pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); @@ -466,13 +577,194 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Press and slide from "ABC" key, enter alphabet shift locked. pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); // Enter/release letter key, remains in alphabet shifted. pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); // Cancel sliding, switch back to symbols. - stopSlidingAndCancel(SYMBOLS); + stopSlidingAndCancel(SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols -> "=\<" key + letter -> symbols -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press and slide from "=\<" key, enter symbols shifted. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Enter/release symbols shift letter key, remains in symbols shifted. + pressAndSlideFromKey('|', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Cancel sliding, switch back to symbols. + stopSlidingAndCancel(SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + } + + // Sliding input in symbols shifted. + public void testSlidingSymbolsShifted() { + // Symbols shifted -> "?123" + letter -> symbols shifted. + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from shift key, enter symbols. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Enter/release symbol letter keys, switch back to symbols shifted. + pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + stopSlidingOnKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED); + + // Symbols shifted -> "ABC" key + letter -> symbols shifted. + // Press and slide from "ABC" key, enter alphabet. + pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Enter/release letter keys, switch back to symbols shifted. + pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Alphabet shifted -> symbols shifted -> "ABC" + letter -> symbols shifted -> + // alphabet. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Press/release shift key, enter alphabet shifted. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from "ABC" key. + pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Enter/release letter keys, switch back to symbols shifted. + pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + stopSlidingOnKey('a', ALPHABET_UNSHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet (not alphabet shifted). + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Alphabet shift locked -> symbols shifted -> "ABC" + letter -> symbols shifted -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from "ABC" key. + pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + // Enter/release letter keys, switch back to symbols shifted. + pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + stopSlidingOnKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols shifted -> "?123" + letter -> symbols shifted -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from "?123" key. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Enter/release symbol letter keys, switch back to symbols shifted. + pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + stopSlidingOnKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + } + + // Cancel sliding input in symbols shifted. + public void testSlidingSymbolsShiftedCancel() { + // Symbols shifted -> "?123" + letter -> symbols shifted. + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from shift key, enter symbols. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Enter/release symbol letter key, remains in symbols. + pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Cancel sliding, switch back to symbols shifted. + stopSlidingAndCancel(SYMBOLS_SHIFTED); + + // Symbols shifted -> "ABC" key + letter -> symbols shifted. + // Press and slide from "ABC" key, enter alphabet. + pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Enter/release letter key, remains in alphabet. + pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Cancel sliding, switch back to symbols shifted. + stopSlidingAndCancel(SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Alphabet shifted -> symbols shifted -> "ABC" + letter -> symbols shifted -> + // alphabet. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Press/release shift key, enter alphabet shifted. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from "ABC" key. + pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Enter/release letter key, remains in alphabet. + pressAndSlideFromKey('z', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Cancel sliding, switch back to symbols shifted. + stopSlidingAndCancel(SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet (not alphabet shifted). + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Alphabet shift locked -> symbols shifted -> "ABC" + letter -> symbols shifted -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from "ABC" key. + pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + // Enter/release letter key, remains in alphabet shift locked. + pressAndSlideFromKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + // Cancel sliding, switch back to symbols shifted. + stopSlidingAndCancel(SYMBOLS_SHIFTED); + // Press/release "ABC" key, switch to alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols shifted -> "?123" + letter -> symbols shifted -> + // alphabet shift locked. + // Load keyboard + loadKeyboard(ALPHABET_UNSHIFTED); + // Long press shift key, enter alphabet shift locked. + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press and slide from "?123" key. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Enter/release symbol letter key, remains in symbols. + pressAndSlideFromKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Cancel sliding, switch back to symbols shifted. + stopSlidingAndCancel(SYMBOLS_SHIFTED); // Press/release "ABC" key, switch to alphabet shift locked. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); } @@ -491,7 +783,14 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { loadKeyboard(ALPHABET_UNSHIFTED); // Press/release "?123" key. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_UNSHIFTED); + + // Press/release "?123" key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); // Change focus to new text field. loadKeyboard(ALPHABET_UNSHIFTED); } @@ -515,7 +814,14 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Press/release "?123" key. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release "?123" key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); // Change focus to new text field. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); } @@ -554,11 +860,49 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> symbols -> rotate -> symbols -> // Alphabet shift locked. // Press/release "?123" key, enter symbols. - pressAndReleaseKey(CODE_SYMBOL, SYMBOLS, SYMBOLS); + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); // Rotate device, remain in symbols, - rotateDevice(SYMBOLS); + rotateDevice(SYMBOLS_UNSHIFTED); + // Press/release "ABC" key, alphabet shift locked state should be maintained. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols shifted -> rotate -> symbols shifted -> + // Alphabet shift locked. + // Press/release "?123" key, enter symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Rotate device, remain in symbols shifted. + rotateDevice(SYMBOLS_SHIFTED); // Press/release "ABC" key, alphabet shift locked state should be maintained. pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Alphabet shift locked -> symbols shifted -> alphabet shift locked -> rotate -> + // Alphabet shift locked -> symbols. + // Press/release "?123" key, enter symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, enter alphabet shift locked. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + // Rotate device, remain in alphabet shift locked. + rotateDevice(ALPHABET_SHIFT_LOCKED); + // Press/release "?123" key, enter symbols (not symbols shifted). + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Alphabet -> symbols shifted -> alphabet -> rotate -> + // Alphabet -> symbols. + loadKeyboard(ALPHABET_UNSHIFTED); + // Press/release "?123" key, enter symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "=\<" key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Press/release "ABC" key, enter alphabet. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + // Rotate device, remain in alphabet shift locked. + rotateDevice(ALPHABET_UNSHIFTED); + // Press/release "?123" key, enter symbols (not symbols shifted). + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); } // Rapidly type shift key. diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java index b209b0085..6e3e37add 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java @@ -43,7 +43,8 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { public static final int ALPHABET_AUTOMATIC_SHIFTED = 2; public static final int ALPHABET_SHIFT_LOCKED = 3; public static final int ALPHABET_SHIFT_LOCK_SHIFTED = 4; - public static final int SYMBOLS = 5; + public static final int SYMBOLS_UNSHIFTED = 5; + public static final int SYMBOLS_SHIFTED = 6; } private int mLayout = MockConstants.ALPHABET_UNSHIFTED; @@ -68,7 +69,8 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { case MockConstants.ALPHABET_AUTOMATIC_SHIFTED: return "ALPHABET_AUTOMATIC_SHIFTED"; case MockConstants.ALPHABET_SHIFT_LOCKED: return "ALPHABET_SHIFT_LOCKED"; case MockConstants.ALPHABET_SHIFT_LOCK_SHIFTED: return "ALPHABET_SHIFT_LOCK_SHIFTED"; - case MockConstants.SYMBOLS: return "SYMBOLS"; + case MockConstants.SYMBOLS_UNSHIFTED: return "SYMBOLS_UNSHIFTED"; + case MockConstants.SYMBOLS_SHIFTED: return "SYMBOLS_SHIFTED"; default: return "UNKNOWN<" + layoutId + ">"; } } @@ -109,7 +111,12 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { @Override public void setSymbolsKeyboard() { - mLayout = MockConstants.SYMBOLS; + mLayout = MockConstants.SYMBOLS_UNSHIFTED; + } + + @Override + public void setSymbolsShiftedKeyboard() { + mLayout = MockConstants.SYMBOLS_SHIFTED; } @Override diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java new file mode 100644 index 000000000..e2e148903 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.inputmethod.latin.makedict.DictEncoder; +import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FusionDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.makedict.Ver3DictEncoder; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; + +@LargeTest +public class BinaryDictionaryTests extends AndroidTestCase { + private static final FormatSpec.FormatOptions FORMAT_OPTIONS = + new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */); + private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; + private static final String TEST_LOCALE = "test"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + private File createEmptyDictionaryAndGetFile(final String filename) throws IOException, + UnsupportedFormatException { + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); + final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION, + getContext().getCacheDir()); + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); + return file; + } + + public void testIsValidDictionary() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } catch (UnsupportedFormatException e) { + fail("UnsupportedFormatException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + assertTrue("binaryDictionary must be valid for existing valid dictionary file.", + binaryDictionary.isValidDictionary()); + binaryDictionary.close(); + assertFalse("binaryDictionary must be invalid after closing.", + binaryDictionary.isValidDictionary()); + dictFile.delete(); + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, + dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), + TEST_LOCALE, true /* isUpdatable */); + assertFalse("binaryDictionary must be invalid for not existing dictionary file.", + binaryDictionary.isValidDictionary()); + binaryDictionary.close(); + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 0a1c4e963..2603b35f5 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -44,9 +44,9 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { private static final String PREF_DEBUG_MODE = "debug_mode"; - // The message that sets the underline is posted with a 100 ms delay + // The message that sets the underline is posted with a 200 ms delay protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 200; - // The message that sets predictions is posted with a 100 ms delay + // The message that sets predictions is posted with a 200 ms delay protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200; protected LatinIME mLatinIME; diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index 1fd1b8a81..d15e88bdb 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -77,19 +77,34 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { private void addToDict(final UserHistoryPredictionDictionary dict, final List<String> words) { String prevWord = null; for (String word : words) { - dict.forceAddWordForTest(prevWord, word, true); + dict.addToPersonalizationPredictionDictionary(prevWord, word, true); prevWord = word; } } + /** + * @param checksContents if true, checks whether written words are actually in the dictionary + * or not. + */ private void addAndWriteRandomWords(final String testFilenameSuffix, final int numberOfWords, - final Random random) { + final Random random, final boolean checksContents) { final List<String> words = generateWords(numberOfWords, random); final UserHistoryPredictionDictionary dict = PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(), testFilenameSuffix /* locale */, mPrefs); // Add random words to the user history dictionary. addToDict(dict, words); + if (checksContents) { + try { + Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + } + for (int i = 0; i < 10 && i < numberOfWords; ++i) { + final String word = words.get(i); + // This may fail as long as we use tryLock on inserting the bigram words + assertTrue(dict.isInDictionaryForTests(word)); + } + } // write to file. dict.close(); } @@ -103,7 +118,8 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final Random random = new Random(123456); try { - addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random); + addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random, + true /* checksContents */); } finally { try { Log.d(TAG, "waiting for writing ..."); @@ -148,7 +164,8 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final int index = i % numberOfLanguages; // Switch languages to testFilenameSuffixes[index]. addAndWriteRandomWords(testFilenameSuffixes[index], - numberOfWordsInsertedForEachLanguageSwitch, random); + numberOfWordsInsertedForEachLanguageSwitch, random, + false /* checksContents */); } final long end = System.currentTimeMillis(); diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java new file mode 100644 index 000000000..7fd167977 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 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.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +@MediumTest +public class AsyncResultHolderTests extends AndroidTestCase { + private static final String TAG = AsyncResultHolderTests.class.getSimpleName(); + + private static final int TIMEOUT_IN_MILLISECONDS = 500; + private static final int MARGIN_IN_MILLISECONDS = 250; + private static final int DEFAULT_VALUE = 2; + private static final int SET_VALUE = 1; + + private <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value, + final long time) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping", e); + } + holder.set(value); + } + }).start(); + } + + public void testGetWithoutSet() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(DEFAULT_VALUE, resultValue); + } + + public void testGetBeforeSet() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(DEFAULT_VALUE, resultValue); + } + + public void testGetAfterSet() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + holder.set(SET_VALUE); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(SET_VALUE, resultValue); + } + + public void testGetBeforeTimeout() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(SET_VALUE, resultValue); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java new file mode 100644 index 000000000..e0755483c --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 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.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unit tests for PrioritizedSerialExecutor. + * TODO: Add more detailed tests to make use of priorities, etc. + */ +@MediumTest +public class PrioritizedSerialExecutorTests extends AndroidTestCase { + private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName(); + + private static final int NUM_OF_TASKS = 10; + private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; + + public void testExecute() { + final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.execute(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + try { + Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(NUM_OF_TASKS, v.get()); + } + + public void testExecutePrioritized() { + final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.executePrioritized(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + try { + Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(NUM_OF_TASKS, v.get()); + } + + public void testExecuteCombined() { + final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.execute(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.executePrioritized(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + + try { + Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(2 * NUM_OF_TASKS, v.get()); + } +} diff --git a/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml index 3067a1d01..feaed4c98 100644 --- a/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml @@ -25,8 +25,9 @@ <string name="label_to_alpha_key">אבג</string> <!-- U+2605: "★" BLACK STAR --> <string name="more_keys_for_star">★</string> - <!-- U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN --> - <string name="more_keys_for_plus">﬩</string> + <!-- U+00B1: "±" PLUS-MINUS SIGN + U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN --> + <string name="more_keys_for_plus">±,﬩</string> <!-- The all letters need to be mirrored are found at http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt --> <string name="more_keys_for_left_parenthesis">!fixedColumnOrder!3,<|>,{|},[|]</string> diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml index d3bcfd34a..a4c2f126d 100644 --- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml @@ -88,7 +88,8 @@ U+2666: "♦" BLACK DIAMOND SUIT U+2663: "♣" BLACK CLUB SUIT --> <string name="more_keys_for_bullet">♪,♥,♠,♦,♣</string> - <string name="more_keys_for_plus"></string> + <!-- U+00B1: "±" PLUS-MINUS SIGN --> + <string name="more_keys_for_plus">±</string> <!-- The all letters need to be mirrored are found at http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt --> <string name="more_keys_for_left_parenthesis">!fixedColumnOrder!3,<,{,[</string> @@ -169,7 +170,8 @@ <string name="keyhintlabel_for_tablet_comma"></string> <string name="more_keys_for_tablet_comma"></string> <string name="keyhintlabel_for_tablet_period"></string> - <string name="more_keys_for_tablet_period"></string> + <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> + <string name="more_keys_for_tablet_period">…</string> <string name="keylabel_for_apostrophe">\'</string> <string name="keyhintlabel_for_apostrophe">\"</string> <string name="more_keys_for_apostrophe">\"</string> @@ -185,6 +187,10 @@ <string name="shortcut_as_more_key">!icon/shortcut_key|!code/key_shortcut</string> <string name="action_next_as_more_key">!hasLabels!,\@string/label_next_key|!code/key_action_next</string> <string name="action_previous_as_more_key">!hasLabels!,\@string/label_previous_key|!code/key_action_previous</string> + <!-- Label for "switch to more symbol" modifier key. Must be short to fit on key! --> + <string name="label_to_more_symbol_key">= \\ <</string> + <!-- Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! --> + <string name="label_to_more_symbol_for_tablet_key">~ [ {</string> <!-- Label for "Tab" key. Must be short to fit on key! --> <string name="label_tab_key">Tab</string> <!-- Label for "switch to phone numeric" key. Must be short to fit on key! --> |