diff options
Diffstat (limited to 'java')
35 files changed, 1588 insertions, 252 deletions
diff --git a/java/res/layout/emoji_keyboard_view.xml b/java/res/layout/emoji_keyboard_view.xml index 5fee419d0..36909a159 100644 --- a/java/res/layout/emoji_keyboard_view.xml +++ b/java/res/layout/emoji_keyboard_view.xml @@ -72,6 +72,11 @@ android:id="@+id/emoji_keyboard_pager" android:layout_width="match_parent" android:layout_height="wrap_content" /> + <com.android.inputmethod.keyboard.EmojiCategoryPageIndicatorView + android:id="@+id/emoji_category_page_id_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/emoji_category_page_id_view_background" /> <LinearLayout android:id="@+id/emoji_action_bar" android:orientation="horizontal" diff --git a/java/res/values-km/donottranslate.xml b/java/res/values-km/donottranslate.xml new file mode 100644 index 000000000..a9893feec --- /dev/null +++ b/java/res/values-km/donottranslate.xml @@ -0,0 +1,23 @@ +<?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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Whether this language uses spaces between words --> + <bool name="current_language_has_spaces">false</bool> +</resources> diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml index ea762f977..a37f28206 100644 --- a/java/res/values/colors.xml +++ b/java/res/values/colors.xml @@ -55,4 +55,6 @@ <color name="setup_text_action">@android:color/holo_blue_light</color> <color name="setup_step_background">@android:color/background_light</color> <color name="setup_welcome_video_margin_color">#FFCCCCCC</color> + <color name="emoji_category_page_id_view_background">#FF000000</color> + <color name="emoji_category_page_id_view_foreground">#80FFFFFF</color> </resources> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index 88e327f26..4e3b2f567 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -113,13 +113,14 @@ <dimen name="gesture_floating_preview_text_offset">73dp</dimen> <dimen name="gesture_floating_preview_horizontal_padding">24dp</dimen> <dimen name="gesture_floating_preview_vertical_padding">16dp</dimen> - <dimen name="gesture_floating_preview_round_radius">3dp</dimen> + <dimen name="gesture_floating_preview_round_radius">2dp</dimen> <!-- Emoji keyboard --> <fraction name="emoji_keyboard_key_width">14.2857%p</fraction> <fraction name="emoji_keyboard_row_height">33%p</fraction> <fraction name="emoji_keyboard_key_letter_size">90%p</fraction> <integer name="emoji_keyboard_max_key_count">21</integer> + <dimen name="emoji_category_page_id_height">3dp</dimen> <!-- Inset used in Accessibility mode to avoid accidental key presses when a finger slides off the screen. --> <dimen name="accessibility_edge_slop">8dp</dimen> diff --git a/java/res/xml-sw600dp/row_pcqwerty5.xml b/java/res/xml-sw600dp/row_pcqwerty5.xml index a79d2a87f..b854f1051 100644 --- a/java/res/xml-sw600dp/row_pcqwerty5.xml +++ b/java/res/xml-sw600dp/row_pcqwerty5.xml @@ -53,7 +53,7 @@ latin:keyXPos="-9.0%p" latin:keyWidth="9.0%p" latin:backgroundType="functional" - latin:keyboardLayout="@xml/key_symbols_period" /> + latin:keyboardLayout="@xml/key_f2" /> </default> </switch> </Row> diff --git a/java/res/xml-sw600dp/rows_khmer.xml b/java/res/xml-sw600dp/rows_khmer.xml new file mode 100644 index 000000000..2824a5c2f --- /dev/null +++ b/java/res/xml-sw600dp/rows_khmer.xml @@ -0,0 +1,72 @@ +<?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" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="7.5%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="7.5%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer2" /> + </Row> + <Row + latin:keyWidth="7.5%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer3" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="7.5%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_khmer4" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <Spacer /> + </case> + <default> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </default> + </switch> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw600dp/rows_lao.xml b/java/res/xml-sw600dp/rows_lao.xml index cfe8db98e..446d9bd5a 100644 --- a/java/res/xml-sw600dp/rows_lao.xml +++ b/java/res/xml-sw600dp/rows_lao.xml @@ -55,8 +55,17 @@ latin:keyWidth="10.0%p" /> <include latin:keyboardLayout="@xml/rowkeys_lao4" /> - <include - latin:keyboardLayout="@xml/keys_exclamation_question" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <Spacer /> + </case> + <default> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </default> + </switch> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml-sw600dp/rows_thai.xml b/java/res/xml-sw600dp/rows_thai.xml index cfcaf6815..7738c7f04 100644 --- a/java/res/xml-sw600dp/rows_thai.xml +++ b/java/res/xml-sw600dp/rows_thai.xml @@ -59,8 +59,17 @@ latin:keyWidth="10.0%p" /> <include latin:keyboardLayout="@xml/rowkeys_thai4" /> - <include - latin:keyboardLayout="@xml/keys_exclamation_question" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <Spacer /> + </case> + <default> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </default> + </switch> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml/kbd_khmer.xml b/java/res/xml/kbd_khmer.xml new file mode 100644 index 000000000..7a2337a48 --- /dev/null +++ b/java/res/xml/kbd_khmer.xml @@ -0,0 +1,31 @@ +<?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. +*/ +--> + +<Keyboard + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:rowHeight="20%p" + latin:verticalGap="@fraction/key_bottom_gap_5row" + latin:keyLetterSize="@fraction/key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row" + latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" +> + <include + latin:keyboardLayout="@xml/rows_khmer" /> +</Keyboard> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index 6b3dc9a0d..67ed9620d 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -184,4 +184,11 @@ latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/more_keys_for_punctuation" latin:backgroundType="functional" /> + <key-style + latin:styleName="comKeyStyle" + latin:keyLabel="!text/keylabel_for_popular_domain" + latin:keyLabelFlags="autoXScale|fontNormal|hasPopupHint|preserveCase" + latin:keyOutputText="!text/keylabel_for_popular_domain" + latin:moreKeys="!text/more_keys_for_popular_domain" + latin:backgroundType="functional" /> </merge> diff --git a/java/res/xml/keyboard_layout_set_khmer.xml b/java/res/xml/keyboard_layout_set_khmer.xml new file mode 100644 index 000000000..181f98b3d --- /dev/null +++ b/java/res/xml/keyboard_layout_set_khmer.xml @@ -0,0 +1,58 @@ +<?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. +*/ +--> + +<KeyboardLayoutSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_khmer" + latin:enableProximityCharsCorrection="true" /> + <Element + latin:elementName="alphabetAutomaticShifted" + latin:elementKeyboard="@xml/kbd_khmer" + latin:enableProximityCharsCorrection="true" /> + <!-- On these shifted alphabet layouts the proximity characters correction should be disabled + because the letters on these layouts aren't the ones in different case of the above + unshifted layouts. --> + <Element + latin:elementName="alphabetManualShifted" + latin:elementKeyboard="@xml/kbd_khmer" /> + <Element + latin:elementName="alphabetShiftLocked" + latin:elementKeyboard="@xml/kbd_khmer" /> + <Element + latin:elementName="alphabetShiftLockShifted" + latin:elementKeyboard="@xml/kbd_khmer" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneSymbols" + latin:elementKeyboard="@xml/kbd_phone_symbols" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardLayoutSet> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index d7424c0c7..945fbd537 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -54,6 +54,7 @@ iw: Hebrew/hebrew # "he" is official language code of Hebrew. ka: Georgian/georgian (kk: Kazakh/east_slavic) # disabled temporarily. waiting for strnig resources. + km: Khmer/khmer ky: Kyrgyz/east_slavic lo: Lao/lao lt: Lithuanian/qwerty @@ -62,8 +63,8 @@ mn: Mongolian/mongolian ms: Malay/qwerty nb: Norwegian Bokmål/nordic - ne: Nepali Romanized/nepali_romanized - ne: Nepali Traditional/nepali_traditional + ne: Nepali Romanized/nepali_romanized # disabled temporarily + ne: Nepali Traditional/nepali_traditional # disabled temporarily nl: Dutch/qwerty nl_BE: Dutch Belgium/azerty pl: Polish/qwerty @@ -326,6 +327,14 @@ --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:subtypeId="0x1365683a" + android:imeSubtypeLocale="km" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=khmer,EmojiCapable" + /> + <!-- android:subtypeId="Need this for km" --> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:subtypeId="0x2e391c04" android:imeSubtypeLocale="ky" android:imeSubtypeMode="keyboard" @@ -380,6 +389,7 @@ android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> + <!-- <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:subtypeId="0xd80a4cee" @@ -394,6 +404,7 @@ android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional,EmojiCapable" /> + --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:subtypeId="0x3f9fd91e" diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml index 0e618059e..4ec908ba1 100644 --- a/java/res/xml/row_pcqwerty5.xml +++ b/java/res/xml/row_pcqwerty5.xml @@ -51,13 +51,13 @@ latin:keyWidth="11.538%p" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="42.310%p" /> + latin:keyWidth="38.464%p" /> </case> <!-- languageSwitchKeyEnabled="false" --> <default> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="53.848%p" /> + latin:keyWidth="50.002%p" /> </default> </switch> <Key @@ -71,9 +71,9 @@ </case> <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" --> <default> - <include - latin:keyboardLayout="@xml/key_symbols_period" - latin:backgroundType="functional" /> + <Key + latin:keyStyle="emojiKeyStyle" + latin:keyWidth="fillRight" /> </default> </switch> </Row> diff --git a/java/res/xml/rowkeys_khmer1.xml b/java/res/xml/rowkeys_khmer1.xml new file mode 100644 index 000000000..174ac757b --- /dev/null +++ b/java/res/xml/rowkeys_khmer1.xml @@ -0,0 +1,191 @@ +<?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:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+200D: ZERO WIDTH JOINER --> + <Key + latin:keyLabel="!" + latin:moreKeys="!icon/zwj_key|‍" /> + <!-- U+17D7: "ៗ" KHMER SIGN LEK TOO + U+200C: ZERO WIDTH NON-JOINER --> + <Key + latin:keyLabel="ៗ" + latin:moreKeys="!icon/zwnj_key|‌" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D1: "៑" KHMER SIGN VIRIAM --> + <Key + latin:keyLabel=""" + latin:keyHintLabel="៑" + latin:moreKeys="៑" /> + <!-- U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL + U+20AC: "€" EURO SIGN --> + <Key + latin:keyLabel="៛" + latin:keyHintLabel="$" + latin:moreKeys="$,€" /> + <!-- U+17D6: "៖" KHMER SIGN CAMNUC PII KUUH --> + <Key + latin:keyLabel="%" + latin:keyHintLabel="៖" + latin:moreKeys="៖" /> + <!-- U+17CD: "៍" KHMER SIGN TOANDAKHIAT + U+17D9: "៙" KHMER SIGN PHNAEK MUAN --> + <Key + latin:keyLabel="៍" + latin:keyHintLabel="៙" + latin:moreKeys="៙" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D0: "័" KHMER SIGN SAMYOK SANNYA + U+17DA: "៚" KHMER SIGN KOOMUUT --> + <Key + latin:keyLabel="័" + latin:keyHintLabel="៚" + latin:moreKeys="៚" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CF: "៏" KHMER SIGN AHSDA --> + <Key + latin:keyLabel="៏" + latin:keyHintLabel="*" + latin:moreKeys="*" + latin:keyLabelFlags="fontNormal" /> + <!-- U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <Key + latin:keyLabel="(" + latin:keyHintLabel="{" + latin:moreKeys="{,«" /> + <!-- U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <Key + latin:keyLabel=")" + latin:keyHintLabel="}" + latin:moreKeys="},»" /> + <!-- U+17CC: "៌" KHMER SIGN ROBAT + U+00D7: "×" MULTIPLICATION SIGN --> + <Key + latin:keyLabel="៌" + latin:keyHintLabel="×" + latin:moreKeys="×" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CE: "៎" KHMER SIGN KAKABAT --> + <Key + latin:keyLabel="៎" + latin:keyLabelFlags="fontNormal" /> + </case> + <default> + <!-- U+17E1: "១" KHMER DIGIT ONE + U+17F1: "៱" KHMER SYMBOL LEK ATTAK MUOY --> + <Key + latin:keyLabel="១" + latin:keyHintLabel="1" + latin:additionalMoreKeys="1" + latin:moreKeys="៱" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E2: "២" KHMER DIGIT TWO + U+17F2: "៲" KHMER SYMBOL LEK ATTAK PII --> + <Key + latin:keyLabel="២" + latin:keyHintLabel="2" + latin:additionalMoreKeys="2" + latin:moreKeys="៲" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E3: "៣" KHMER DIGIT THREE + U+17F3: "៳" KHMER SYMBOL LEK ATTAK BEI --> + <Key + latin:keyLabel="៣" + latin:keyHintLabel="3" + latin:additionalMoreKeys="3" + latin:moreKeys="៳" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E4: "៤" KHMER DIGIT FOUR + U+17F4: "៴" KHMER SYMBOL LEK ATTAK BUON --> + <Key + latin:keyLabel="៤" + latin:keyHintLabel="4" + latin:additionalMoreKeys="4" + latin:moreKeys="៴" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E5: "៥" KHMER DIGIT FIVE + U+17F5: "៵" KHMER SYMBOL LEK ATTAK PRAM --> + <Key + latin:keyLabel="៥" + latin:keyHintLabel="5" + latin:additionalMoreKeys="5" + latin:moreKeys="៵" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E6: "៦" KHMER DIGIT SIX + U+17F6: "៶" KHMER SYMBOL LEK ATTAK PRAM-MUOY --> + <Key + latin:keyLabel="៦" + latin:keyHintLabel="6" + latin:additionalMoreKeys="6" + latin:moreKeys="៶" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E7: "៧" KHMER DIGIT SEVEN + U+17F7: "៷" KHMER SYMBOL LEK ATTAK PRAM-PII --> + <Key + latin:keyLabel="៧" + latin:keyHintLabel="7" + latin:additionalMoreKeys="7" + latin:moreKeys="៷" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E8: "៨" KHMER DIGIT EIGHT + U+17F8: "៸" KHMER SYMBOL LEK ATTAK PRAM-BEI --> + <Key + latin:keyLabel="៨" + latin:keyHintLabel="8" + latin:additionalMoreKeys="8" + latin:moreKeys="៸" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E9: "៩" KHMER DIGIT NINE + U+17F9: "៹" KHMER SYMBOL LEK ATTAK PRAM-BUON --> + <Key + latin:keyLabel="៩" + latin:keyHintLabel="9" + latin:additionalMoreKeys="9" + latin:moreKeys="៹" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E0: "០" KHMER DIGIT ZERO + U+17F0: "៰" KHMER SYMBOL LEK ATTAK SON --> + <Key + latin:keyLabel="០" + latin:keyHintLabel="0" + latin:additionalMoreKeys="0" + latin:moreKeys="៰" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A5: "ឥ" KHMER INDEPENDENT VOWEL QI + U+17A6: "ឦ" KHMER INDEPENDENT VOWEL QII --> + <Key + latin:keyLabel="ឥ" + latin:moreKeys=".,ឦ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B2: "ឲ" KHMER INDEPENDENT VOWEL QOO TYPE TWO + U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE --> + <Key + latin:keyLabel="ឲ" + latin:moreKeys="\\,,ឱ" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rowkeys_khmer2.xml b/java/res/xml/rowkeys_khmer2.xml new file mode 100644 index 000000000..cba2d3b90 --- /dev/null +++ b/java/res/xml/rowkeys_khmer2.xml @@ -0,0 +1,144 @@ +<?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:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+1788: "ឈ" KHMER LETTER CHO + U+17DC: "ៜ" KHMER SIGN AVAKRAHASANYA --> + <Key + latin:keyLabel="ឈ" + latin:keyHintLabel="ៜ" + latin:moreKeys="ៜ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BA: "ឺ" KHMER VOWEL SIGN YY + U+17DD: "៝" KHMER SIGN ATTHACAN --> + <Key + latin:keyLabel="ឺ" + latin:keyHintLabel="៝" + latin:moreKeys="៝" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C2: "ែ" KHMER VOWEL SIGN AE --> + <Key + latin:keyLabel="ែ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AC: "ឬ" KHMER INDEPENDENT VOWEL RYY + U+17AB: "ឫ" KHMER INDEPENDENT VOWEL RY --> + <Key + latin:keyLabel="ឬ" + latin:keyHintLabel="ឫ" + latin:moreKeys="ឫ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1791: "ទ" KHMER LETTER TO --> + <Key + latin:keyLabel="ទ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BD: "ួ" KHMER VOWEL SIGN UA --> + <Key + latin:keyLabel="ួ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BC: "ូ" KHMER VOWEL SIGN UU --> + <Key + latin:keyLabel="ូ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B8: "ី" KHMER VOWEL SIGN II --> + <Key + latin:keyLabel="ី" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C5: "ៅ" KHMER VOWEL SIGN AU --> + <Key + latin:keyLabel="ៅ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1797: "ភ" KHMER LETTER PHO --> + <Key + latin:keyLabel="ភ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BF: "ឿ" KHMER VOWEL SIGN YA --> + <Key + latin:keyLabel="ឿ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI --> + <Key + latin:keyLabel="ឰ" + latin:keyLabelFlags="fontNormal" /> + </case> + <default> + <!-- U+1786: "ឆ" KHMER LETTER CHA --> + <Key + latin:keyLabel="ឆ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B9: "ឹ" KHMER VOWEL SIGN Y --> + <Key + latin:keyLabel="ឹ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C1: "េ" KHMER VOWEL SIGN E --> + <Key + latin:keyLabel="េ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179A: "រ" KHMER LETTER RO --> + <Key + latin:keyLabel="រ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178F: "ត" KHMER LETTER TA --> + <Key + latin:keyLabel="ត" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1799: "យ" KHMER LETTER YO --> + <Key + latin:keyLabel="យ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BB: "ុ" KHMER VOWEL SIGN U --> + <Key + latin:keyLabel="ុ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B7: "ិ" KHMER VOWEL SIGN I --> + <Key + latin:keyLabel="ិ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C4: "ោ" KHMER VOWEL SIGN OO --> + <Key + latin:keyLabel="ោ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1795: "ផ" KHMER LETTER PHA --> + <Key + latin:keyLabel="ផ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C0: "ៀ" KHMER VOWEL SIGN IE --> + <Key + latin:keyLabel="ៀ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AA: "ឪ" KHMER INDEPENDENT VOWEL QUUV + U+17A7: "ឧ" KHMER INDEPENDENT VOWEL QU + U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE + U+17B3: "ឳ" KHMER INDEPENDENT VOWEL QAU + U+17A9: "ឩ" KHMER INDEPENDENT VOWEL QUU + U+17A8: "ឨ" KHMER INDEPENDENT VOWEL QUK --> + <Key + latin:keyLabel="ឪ" + latin:keyHintLabel="ឧ" + latin:moreKeys="ឧ,ឱ,ឳ,ឩ,ឨ" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rowkeys_khmer3.xml b/java/res/xml/rowkeys_khmer3.xml new file mode 100644 index 000000000..5d55b9c63 --- /dev/null +++ b/java/res/xml/rowkeys_khmer3.xml @@ -0,0 +1,138 @@ +<?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:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+17B6/U+17C6: "ាំ" KHMER VOWEL SIGN AA/KHMER SIGN NIKAHIT --> + <Key + latin:keyLabel="ាំ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> + <!-- U+17C3: "ៃ" KHMER VOWEL SIGN AI --> + <Key + latin:keyLabel="ៃ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178C: "ឌ" KHMER LETTER DO --> + <Key + latin:keyLabel="ឌ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1792: "ធ" KHMER LETTER THO --> + <Key + latin:keyLabel="ឌ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A2: "អ" KHMER LETTER QA --> + <Key + latin:keyLabel="អ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C7: "ះ" KHMER SIGN REAHMUK + U+17C8: "ៈ" KHMER SIGN YUUKALEAPINTU;--> + <Key + latin:keyLabel="ះ" + latin:keyHintLabel="ៈ" + latin:moreKeys="ៈ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1789: "ញ" KHMER LETTER NYO --> + <Key + latin:keyLabel="ញ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1782: "គ" KHMER LETTER KO + U+179D: "ឝ" KHMER LETTER SHA --> + <Key + latin:keyLabel="គ" + latin:keyHintLabel="ឝ" + latin:moreKeys="ឝ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A1: "ឡ" KHMER LETTER LA --> + <Key + latin:keyLabel="ឡ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C4/U+17C7: "ោះ" KHMER VOWEL SIGN OO/KHMER SIGN REAHMUK --> + <Key + latin:keyLabel="ោះ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C9: "៉" KHMER SIGN MUUSIKATOAN --> + <Key + latin:keyLabel="៉" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AF: "ឯ" KHMER INDEPENDENT VOWEL QE --> + <Key + latin:keyLabel="ឯ" + latin:keyLabelFlags="fontNormal" /> + </case> + <default> + <!-- U+17B6: "ា" KHMER VOWEL SIGN AA --> + <Key + latin:keyLabel="ា" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179F: "ស" KHMER LETTER SA --> + <Key + latin:keyLabel="ស" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178A: "ដ" KHMER LETTER DA --> + <Key + latin:keyLabel="ដ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1790: "ថ" KHMER LETTER THA --> + <Key + latin:keyLabel="ថ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1784: "ង" KHMER LETTER NGO --> + <Key + latin:keyLabel="ង" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A0: "ហ" KHMER LETTER HA --> + <Key + latin:keyLabel="ហ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D2: "្" KHMER SIGN COENG --> + <Key + latin:keyLabel="្" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1780: "ក" KHMER LETTER KA --> + <Key + latin:keyLabel="ក" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179B: "ល" KHMER LETTER LO --> + <Key + latin:keyLabel="ល" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BE: "ើ" KHMER VOWEL SIGN OE --> + <Key + latin:keyLabel="ើ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CB: "់" KHMER SIGN BANTOC --> + <Key + latin:keyLabel="់" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AE: "ឮ" KHMER INDEPENDENT VOWEL LYY + U+17AD: "ឭ" KHMER INDEPENDENT VOWEL LY + U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI --> + <Key + latin:keyLabel="ឮ" + latin:keyHintLabel="ឭ" + latin:moreKeys="ឭ,ឰ" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rowkeys_khmer4.xml b/java/res/xml/rowkeys_khmer4.xml new file mode 100644 index 000000000..fe6c59125 --- /dev/null +++ b/java/res/xml/rowkeys_khmer4.xml @@ -0,0 +1,113 @@ +<?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:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+178D: "ឍ" KHMER LETTER TTHO --> + <Key + latin:keyLabel="ឍ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1783: "ឃ" KHMER LETTER KHO --> + <Key + latin:keyLabel="ឃ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1787: "ជ" KHMER LETTER CO --> + <Key + latin:keyLabel="ជ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C1/U+17C7: "េះ" KHMER VOWEL SIGN E/KHMER SIGN REAHMUK --> + <Key + latin:keyLabel="េះ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1796: "ព" KHMER LETTER PO + U+179E: "ឞ" KHMER LETTER SSO --> + <Key + latin:keyLabel="ព" + latin:keyHintLabel="ឞ" + latin:moreKeys="ឞ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178E: "ណ" KHMER LETTER NNO --> + <Key + latin:keyLabel="ណ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C6: "ំ" KHMER SIGN NIKAHIT --> + <Key + latin:keyLabel="ំ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BB/U+17C7: "ុះ" KHMER VOWEL SIGN U/KHMER SIGN REAHMUK --> + <Key + latin:keyLabel="ុះ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> + <!-- U+17D5: "៕" KHMER SIGN BARIYOOSAN --> + <Key + latin:keyLabel="៕" + latin:keyLabelFlags="fontNormal" /> + <Key + latin:keyLabel="\?" /> + </case> + <default> + <!-- U+178B: "ឋ" KHMER LETTER TTHA --> + <Key + latin:keyLabel="ឋ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1781: "ខ" KHMER LETTER KHA --> + <Key + latin:keyLabel="ខ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1785: "ច" KHMER LETTER CA --> + <Key + latin:keyLabel="ច" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179C: "វ" KHMER LETTER VO --> + <Key + latin:keyLabel="វ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1794: "ប" KHMER LETTER BA --> + <Key + latin:keyLabel="ប" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1793: "ន" KHMER LETTER NO --> + <Key + latin:keyLabel="ន" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1798: "ម" KHMER LETTER MO --> + <Key + latin:keyLabel="ម" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BB/U+17C6: "ុំ" KHMER VOWEL SIGN U/KHMER SIGN NIKAHIT --> + <Key + latin:keyLabel="ុំ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> + <!-- U+17D4: "។" KHMER SIGN KHAN --> + <Key + latin:keyLabel="។" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CA: "៊" KHMER SIGN TRIISAP --> + <Key + latin:keyLabel="៊" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rows_khmer.xml b/java/res/xml/rows_khmer.xml new file mode 100644 index 000000000..e3993871b --- /dev/null +++ b/java/res/xml/rows_khmer.xml @@ -0,0 +1,56 @@ +<?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" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="8.3333%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer1" /> + </Row> + <Row + latin:keyWidth="8.3333%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer2" /> + </Row> + <Row + latin:keyWidth="8.3333%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer3" /> + </Row> + <Row + latin:keyWidth="8.3333%p" + > + <Key + latin:keyStyle="shiftKeyStyle" /> + <include + latin:keyboardLayout="@xml/rowkeys_khmer4" /> + <Key + latin:keyStyle="deleteKeyStyle" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java new file mode 100644 index 000000000..fed134eb9 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java @@ -0,0 +1,67 @@ +/* + * 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.keyboard; + +import com.android.inputmethod.latin.R; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +public class EmojiCategoryPageIndicatorView extends LinearLayout { + private static final float BOTTOM_MARGIN_RATIO = 0.66f; + private final Paint mPaint = new Paint(); + private int mCategoryPageSize = 0; + private int mCurrentCategoryPageId = 0; + private float mOffset = 0.0f; + + public EmojiCategoryPageIndicatorView(Context context) { + this(context, null /* attrs */); + } + + public EmojiCategoryPageIndicatorView(Context context, AttributeSet attrs) { + super(context, attrs); + mPaint.setColor(context.getResources().getColor( + R.color.emoji_category_page_id_view_foreground)); + } + + public void setCategoryPageId(int size, int id, float offset) { + mCategoryPageSize = size; + mCurrentCategoryPageId = id; + mOffset = offset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mCategoryPageSize == 0) { + // If the category is not set yet, just clear and return. + canvas.drawColor(0); + return; + } + final float height = getHeight(); + final float width = getWidth(); + final float unitWidth = width / mCategoryPageSize; + final float left = unitWidth * mCurrentCategoryPageId + mOffset * unitWidth; + final float top = 0.0f; + final float right = left + unitWidth; + final float bottom = height * BOTTOM_MARGIN_RATIO; + canvas.drawRect(left, top, right, bottom, mPaint); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java index 546fa8140..db65de2ad 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java @@ -80,6 +80,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange private TabHost mTabHost; private ViewPager mEmojiPager; + private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView; private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; @@ -197,6 +198,14 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange return mCurrentCategoryId; } + public int getCurrentCategoryPageSize() { + return getCategoryPageSize(mCurrentCategoryId); + } + + public int getCategoryPageSize(int categoryId) { + return mShownCategories.get(categoryId).mPageCount; + } + public void setCurrentCategoryId(int categoryId) { mCurrentCategoryId = categoryId; } @@ -205,6 +214,10 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mCurrentCategoryPageId = id; } + public int getCurrentCategoryPageId() { + return mCurrentCategoryPageId; + } + public void saveLastTypedCategoryPage() { Settings.writeEmojiCategoryLastTypedId( mPrefs, mCurrentCategoryId, mCurrentCategoryPageId); @@ -435,12 +448,16 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mEmojiPager.setOffscreenPageLimit(0); final Resources res = getResources(); final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res); - emojiLp.setPagerProps(mEmojiPager); + emojiLp.setPagerProperties(mEmojiPager); + + mEmojiCategoryPageIndicatorView = + (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view); + emojiLp.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */); final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar); - emojiLp.setActionBarProps(actionBar); + emojiLp.setActionBarProperties(actionBar); // TODO: Implement auto repeat, using View.OnTouchListener? final ImageView deleteKey = (ImageView)findViewById(R.id.emoji_keyboard_delete); @@ -455,7 +472,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange spaceKey.setBackgroundResource(mKeyBackgroundId); spaceKey.setTag(Constants.CODE_SPACE); spaceKey.setOnClickListener(this); - emojiLp.setKeyProps(spaceKey); + emojiLp.setKeyProperties(spaceKey); final ImageView sendKey = (ImageView)findViewById(R.id.emoji_keyboard_send); sendKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); sendKey.setTag(Constants.CODE_ENTER); @@ -466,6 +483,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange public void onTabChanged(final String tabId) { final int categoryId = mEmojiCategory.getCategoryId(tabId); setCurrentCategoryId(categoryId, false /* force */); + updateEmojiCategoryPageIdView(); } @@ -475,6 +493,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); setCurrentCategoryId(newPos.first /* categoryId */, false /* force */); mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */); + updateEmojiCategoryPageIdView(); } @Override @@ -485,7 +504,23 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { - // Ignore this message. Only want the actual page selected. + final Pair<Integer, Integer> newPos = + mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); + final int newCategoryId = newPos.first; + final int newCategorySize = mEmojiCategory.getCategoryPageSize(newCategoryId); + final int currentCategoryId = mEmojiCategory.getCurrentCategoryId(); + final int currentCategoryPageId = mEmojiCategory.getCurrentCategoryPageId(); + final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageSize(); + if (newCategoryId == currentCategoryId) { + mEmojiCategoryPageIndicatorView.setCategoryPageId( + newCategorySize, newPos.second, positionOffset); + } else if (newCategoryId > currentCategoryId) { + mEmojiCategoryPageIndicatorView.setCategoryPageId( + currentCategorySize, currentCategoryPageId, positionOffset); + } else if (newCategoryId < currentCategoryId) { + mEmojiCategoryPageIndicatorView.setCategoryPageId( + currentCategorySize, currentCategoryPageId, positionOffset - 1); + } } @Override @@ -523,6 +558,15 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mKeyboardActionListener = listener; } + private void updateEmojiCategoryPageIdView() { + if (mEmojiCategoryPageIndicatorView == null) { + return; + } + mEmojiCategoryPageIndicatorView.setCategoryPageId( + mEmojiCategory.getCurrentCategoryPageSize(), + mEmojiCategory.getCurrentCategoryPageId(), 0.0f /* offset */); + } + private void setCurrentCategoryId(final int categoryId, final boolean force) { if (mEmojiCategory.getCurrentCategoryId() == categoryId && !force) { return; diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java index 5570d594d..267fad5cd 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java @@ -30,6 +30,7 @@ public class EmojiLayoutParams { public final int mEmojiPagerHeight; private final int mEmojiPagerBottomMargin; public final int mEmojiKeyboardHeight; + private final int mEmojiCategoryPageIdViewHeight; public final int mEmojiActionBarHeight; public final int mKeyVerticalGap; private final int mKeyHorizontalGap; @@ -47,23 +48,32 @@ public class EmojiLayoutParams { (int) defaultKeyboardHeight, (int) defaultKeyboardHeight); mKeyHorizontalGap = (int) (res.getFraction(R.fraction.key_horizontal_gap_ics, defaultKeyboardWidth, defaultKeyboardWidth)); + mEmojiCategoryPageIdViewHeight = + (int) (res.getDimension(R.dimen.emoji_category_page_id_height)); final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding + mKeyVerticalGap; mEmojiActionBarHeight = ((int) baseheight) / DEFAULT_KEYBOARD_ROWS - (mKeyVerticalGap - mBottomPadding) / 2; - mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight; + mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight + - mEmojiCategoryPageIdViewHeight; mEmojiPagerBottomMargin = mKeyVerticalGap / 2; mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1; } - public void setPagerProps(ViewPager vp) { + public void setPagerProperties(ViewPager vp) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams(); - lp.height = mEmojiPagerHeight - mEmojiPagerBottomMargin; + lp.height = mEmojiKeyboardHeight; lp.bottomMargin = mEmojiPagerBottomMargin; vp.setLayoutParams(lp); } - public void setActionBarProps(LinearLayout ll) { + public void setCategoryPageIdViewProperties(LinearLayout ll) { + final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); + lp.height = mEmojiCategoryPageIdViewHeight; + ll.setLayoutParams(lp); + } + + public void setActionBarProperties(LinearLayout ll) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); lp.height = mEmojiActionBarHeight; lp.topMargin = 0; @@ -71,7 +81,7 @@ public class EmojiLayoutParams { ll.setLayoutParams(lp); } - public void setKeyProps(ImageView ib) { + public void setKeyProperties(ImageView ib) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ib.getLayoutParams(); lp.leftMargin = mKeyHorizontalGap / 2; lp.rightMargin = mKeyHorizontalGap / 2; diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index cd127c760..a0316696c 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -245,8 +245,8 @@ public class ProximityInfo { final int threshold = (int) (defaultWidth * SEARCH_DISTANCE); final int thresholdSquared = threshold * threshold; // Round-up so we don't have any pixels outside the grid - final int fullGridWidth = mGridWidth * mCellWidth; - final int fullGridHeight = mGridHeight * mCellHeight; + final int lastPixelXCoordinate = mGridWidth * mCellWidth - 1; + final int lastPixelYCoordinate = mGridHeight * mCellHeight - 1; // For large layouts, 'neighborsFlatBuffer' is about 80k of memory: gridSize is usually 512, // keycount is about 40 and a pointer to a Key is 4 bytes. This contains, for each cell, @@ -329,22 +329,20 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get final int yMiddleOfTopCell = topPixelWithinThreshold - yDeltaToGrid + halfCellHeight; final int yStart = Math.max(halfCellHeight, yMiddleOfTopCell + (yDeltaToGrid <= halfCellHeight ? 0 : mCellHeight)); - final int yEnd = Math.min(fullGridHeight, keyY + key.getHeight() + threshold); + final int yEnd = Math.min(lastPixelYCoordinate, keyY + key.getHeight() + threshold); final int leftPixelWithinThreshold = keyX - threshold; final int xDeltaToGrid = leftPixelWithinThreshold % mCellWidth; final int xMiddleOfLeftCell = leftPixelWithinThreshold - xDeltaToGrid + halfCellWidth; final int xStart = Math.max(halfCellWidth, xMiddleOfLeftCell + (xDeltaToGrid <= halfCellWidth ? 0 : mCellWidth)); - final int xEnd = Math.min(fullGridWidth, keyX + key.getWidth() + threshold); + final int xEnd = Math.min(lastPixelXCoordinate, keyX + key.getWidth() + threshold); int baseIndexOfCurrentRow = (yStart / mCellHeight) * mGridWidth + (xStart / mCellWidth); for (int centerY = yStart; centerY <= yEnd; centerY += mCellHeight) { int index = baseIndexOfCurrentRow; for (int centerX = xStart; centerX <= xEnd; centerX += mCellWidth) { - // TODO: Remove "index < neighborCountPerCell.length" below. - if (index < neighborCountPerCell.length - && key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { + if (key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { neighborsFlatBuffer[index * keyCount + neighborCountPerCell[index]] = key; ++neighborCountPerCell[index]; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java index 2976e2323..0dd71e2ec 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java @@ -193,7 +193,7 @@ public class DynamicGridKeyboard extends Keyboard { public void updateCorrdinates(final int x, final int y) { mCurrentX = x; mCurrentY = y; - getHitBox().offsetTo(x, y); + getHitBox().set(x, y, x + getWidth(), y + getHeight()); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index a72595f7c..2af2f6995 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -2015,6 +2015,25 @@ public final class KeyboardTextsSet { /* 45 */ "\u0410\u0411\u0412", }; + /* Language km: Khmer */ + private static final String[] LANGUAGE_km = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ + // Label for "switch to alphabetic" key. + // U+1780: "ក" KHMER LETTER KA + // U+1781: "ខ" KHMER LETTER KHA + // U+1782: "គ" KHMER LETTER KO + /* 45 */ "\u1780\u1781\u1782", + /* 46~ */ + null, null, null, null, + /* ~49 */ + // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL + /* 50 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + }; + /* Language ky: Kirghiz */ private static final String[] LANGUAGE_ky = { /* 0~ */ @@ -3407,6 +3426,7 @@ public final class KeyboardTextsSet { "iw", LANGUAGE_iw, /* Hebrew */ "ka", LANGUAGE_ka, /* Georgian */ "kk", LANGUAGE_kk, /* Kazakh */ + "km", LANGUAGE_km, /* Khmer */ "ky", LANGUAGE_ky, /* Kirghiz */ "lo", LANGUAGE_lo, /* Lao */ "lt", LANGUAGE_lt, /* Lithuanian */ diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 2a9076436..fcd7ede1a 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -311,9 +311,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, + final int sessionId) { reloadDictionaryIfRequired(); final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder = @@ -321,14 +322,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { getExecutor(mFilename).executePrioritized(new Runnable() { @Override public void run() { - final ArrayList<SuggestedWordInfo> inMemDictSuggestion = - mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions); + final ArrayList<SuggestedWordInfo> inMemDictSuggestion = composer.isBatchMode() ? + null : mDictionaryWriter.getSuggestionsWithSessionId(composer, prevWord, + proximityInfo, blockOffensiveWords, additionalFeaturesOptions, + sessionId); // TODO: Remove checking mIsUpdatable and use native suggestion. if (mBinaryDictionary != null && !mIsUpdatable) { final ArrayList<SuggestedWordInfo> binarySuggestion = - mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions); + mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, + proximityInfo, blockOffensiveWords, additionalFeaturesOptions, + sessionId); if (inMemDictSuggestion == null) { holder.set(binarySuggestion); } else if (binarySuggestion == null) { @@ -342,11 +345,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } } }); - return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); } @Override + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final String prevWord, final ProximityInfo proximityInfo, + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { + return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions, 0 /* sessionId */); + } + + @Override public boolean isValidWord(final String word) { reloadDictionaryIfRequired(); return isValidWordInner(word); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 9f779eb43..dead53032 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -46,6 +46,7 @@ import android.text.InputType; import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.util.Log; +import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.KeyCharacterMap; @@ -236,6 +237,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; + private static final int ARG2_WITHOUT_TYPED_WORD = 0; + private static final int ARG2_WITH_TYPED_WORD = 1; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -269,7 +272,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) { - latinIme.showSuggestionStrip((SuggestedWords) msg.obj); + if (msg.arg2 == ARG2_WITH_TYPED_WORD) { + final Pair<SuggestedWords, String> p = + (Pair<SuggestedWords, String>) msg.obj; + latinIme.showSuggestionStripWithTypedWord(p.first, p.second); + } else { + latinIme.showSuggestionStrip((SuggestedWords) msg.obj); + } } else { latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj, msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); @@ -331,14 +340,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int arg1 = dismissGestureFloatingPreviewText ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT; - obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords) - .sendToTarget(); + obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, + ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget(); } public void showSuggestionStrip(final SuggestedWords suggestedWords) { removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, - ARG1_NOT_GESTURE_INPUT, 0, suggestedWords).sendToTarget(); + ARG1_NOT_GESTURE_INPUT, ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget(); + } + + // TODO: Remove this method. + public void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords, + final String typedWord) { + removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); + obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, ARG1_NOT_GESTURE_INPUT, + ARG2_WITH_TYPED_WORD, + new Pair<SuggestedWords, String>(suggestedWords, typedWord)).sendToTarget(); } public void onEndBatchInput(final SuggestedWords suggestedWords) { @@ -2468,27 +2486,39 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen false /* isPrediction */); } - private void setAutoCorrection(final SuggestedWords suggestedWords) { + private void setAutoCorrection(final SuggestedWords suggestedWords, final String typedWord) { if (suggestedWords.isEmpty()) return; final String autoCorrection; if (suggestedWords.mWillAutoCorrect) { autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); } else { - autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD); + // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) + // because it may differ from mWordComposer.mTypedWord. + autoCorrection = typedWord; } mWordComposer.setAutoCorrection(autoCorrection); } + private void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords, + final String typedWord) { + if (suggestedWords.isEmpty()) { + clearSuggestionStrip(); + return; + } + setAutoCorrection(suggestedWords, typedWord); + final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); + setSuggestedWords(suggestedWords, isAutoCorrection); + setAutoCorrectionIndicator(isAutoCorrection); + setSuggestionStripShown(isSuggestionsStripVisible()); + } + 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()); + showSuggestionStripWithTypedWord(suggestedWords, + suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)); } private void commitCurrentAutoCorrection(final String separator) { @@ -2766,7 +2796,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Since there is only one word, willAutoCorrect is false. suggestedWords = suggestedWordsIncludingTypedWord; } - unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords); + // We need to pass typedWord because mWordComposer.mTypedWord may differ from + // typedWord. + unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, + typedWord); }}); } else { // We found suggestion spans in the word. We'll create the SuggestedWords out of @@ -2775,12 +2808,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, false /* isPrediction */); - unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords); + // We need to pass typedWord because mWordComposer.mTypedWord may differ from typedWord. + unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, typedWord); } } public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip( - final SuggestedWords suggestedWords) { + final SuggestedWords suggestedWords, final String typedWord) { // Note that it's very important here that suggestedWords.mWillAutoCorrect is false. // We never want to auto-correct on a resumed suggestion. Please refer to the three places // above in restartSuggestionsOnWordTouchedByCursor() where suggestedWords is affected. @@ -2788,7 +2822,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // the text to adapt it. // TODO: remove mIsAutoCorrectionIndicatorOn (see comment on definition) mIsAutoCorrectionIndicatorOn = false; - mHandler.showSuggestionStrip(suggestedWords); + mHandler.showSuggestionStripWithTypedWord(suggestedWords, typedWord); } /** diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 1684d47b5..6c18c948f 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -361,12 +361,6 @@ public final class Suggest { // At second character typed, search the unigrams (scores being affected by bigrams) for (final String key : mDictionaries.keySet()) { - // Skip User history dictionary for lookup - // TODO: The user history dictionary should just override getSuggestionsWithSessionId - // to make sure it doesn't return anything and we should remove this test - if (key.equals(Dictionary.TYPE_USER_HISTORY)) { - continue; - } final Dictionary dictionary = mDictionaries.get(key); suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 5b319ad90..665c7a27c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -498,7 +498,7 @@ public final class BinaryDictDecoderUtils { // reach the end of the array. if (options.mSupportsDynamicUpdate) { - final boolean hasValidForwardLink = dictDecoder.readForwardLinkAndAdvancePosition(); + final boolean hasValidForwardLink = dictDecoder.readAndFollowForwardLink(); if (!hasValidForwardLink) break; } } while (options.mSupportsDynamicUpdate && dictDecoder.hasNextPtNodeArray()); @@ -550,7 +550,7 @@ public final class BinaryDictDecoderUtils { * @return the created (or merged) dictionary. */ @UsedForTesting - /* package */ static FusionDictionary readDictionaryBinary(final Ver3DictDecoder dictDecoder, + /* package */ static FusionDictionary readDictionaryBinary(final DictDecoder dictDecoder, final FusionDictionary dict) throws IOException, UnsupportedFormatException { // Read header final FileHeader fileHeader = dictDecoder.readHeader(); diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index 70931f885..4dba8e5cf 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -368,9 +368,9 @@ public class BinaryDictEncoderUtils { if (null != ptNode.mBigrams) { for (WeightedString bigram : ptNode.mBigrams) { final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray, - nodeSize + size + FormatSpec.PTNODE_FLAGS_SIZE, + nodeSize + size + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE, FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord)); - nodeSize += getByteSize(offset) + FormatSpec.PTNODE_FLAGS_SIZE; + nodeSize += getByteSize(offset) + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE; } } ptNode.mCachedSize = nodeSize; diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 2c5e93e5c..a282f595c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -114,7 +114,7 @@ public final class BinaryDictIOUtils { if (p.mPosition == p.mNumOfPtNode) { if (formatOptions.mSupportsDynamicUpdate) { final boolean hasValidForwardLinkAddress = - dictDecoder.readForwardLinkAndAdvancePosition(); + dictDecoder.readAndFollowForwardLink(); if (hasValidForwardLinkAddress && dictDecoder.hasNextPtNodeArray()) { // The node array has a forward link. p.mNumOfPtNode = Position.NOT_READ_PTNODE_COUNT; @@ -233,7 +233,7 @@ public final class BinaryDictIOUtils { } final boolean hasValidForwardLinkAddress = - dictDecoder.readForwardLinkAndAdvancePosition(); + dictDecoder.readAndFollowForwardLink(); if (!hasValidForwardLinkAddress || !dictDecoder.hasNextPtNodeArray()) { return FormatSpec.NOT_VALID_WORD; } diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java index 40e852423..3796a466c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java @@ -17,9 +17,11 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; import java.io.File; @@ -30,13 +32,50 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; +import java.util.HashMap; import java.util.TreeMap; /** - * An interface of binary dictionary decoder. + * The base class of binary dictionary decoders. */ -public interface DictDecoder { - public FileHeader readHeader() throws IOException, UnsupportedFormatException; +public abstract class DictDecoder { + + protected FileHeader readHeader(final DictBuffer dictBuffer) + throws IOException, UnsupportedFormatException { + if (dictBuffer == null) { + openDictBuffer(); + } + + final int version = HeaderReader.readVersion(dictBuffer); + if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION + || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) { + throw new UnsupportedFormatException("Unsupported version : " + version); + } + // TODO: Remove this field. + final int optionsFlags = HeaderReader.readOptionFlags(dictBuffer); + + final int headerSize = HeaderReader.readHeaderSize(dictBuffer); + + if (headerSize < 0) { + throw new UnsupportedFormatException("header size can't be negative."); + } + + final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer, + headerSize); + + final FileHeader header = new FileHeader(headerSize, + new FusionDictionary.DictionaryOptions(attributes, + 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), + 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), + new FormatOptions(version, + 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); + return header; + } + + /** + * Reads and returns the file header. + */ + public abstract FileHeader readHeader() throws IOException, UnsupportedFormatException; /** * Reads PtNode from nodeAddress. @@ -44,7 +83,7 @@ public interface DictDecoder { * @param formatOptions the format options. * @return PtNodeInfo. */ - public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions); + public abstract PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions); /** * Reads a buffer and returns the memory representation of the dictionary. @@ -59,9 +98,9 @@ public interface DictDecoder { * @return the created (or merged) dictionary. */ @UsedForTesting - public FusionDictionary readDictionaryBinary(final FusionDictionary dict, + public abstract FusionDictionary readDictionaryBinary(final FusionDictionary dict, final boolean deleteDictIfBroken) - throws FileNotFoundException, IOException, UnsupportedFormatException; + throws FileNotFoundException, IOException, UnsupportedFormatException; /** * Gets the address of the last PtNode of the exact matching word in the dictionary. @@ -74,7 +113,12 @@ public interface DictDecoder { */ @UsedForTesting public int getTerminalPosition(final String word) - throws IOException, UnsupportedFormatException; + throws IOException, UnsupportedFormatException { + if (!isDictBufferOpen()) { + openDictBuffer(); + } + return BinaryDictIOUtils.getTerminalPosition(this, word); + } /** * Reads unigrams and bigrams from the binary file. @@ -86,50 +130,56 @@ public interface DictDecoder { * @throws IOException if the file can't be read. * @throws UnsupportedFormatException if the format of the file is not recognized. */ + @UsedForTesting public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words, final TreeMap<Integer, Integer> frequencies, final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams) - throws IOException, UnsupportedFormatException; + throws IOException, UnsupportedFormatException { + if (!isDictBufferOpen()) { + openDictBuffer(); + } + BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams); + } /** * Sets the position of the buffer to the given value. * * @param newPos the new position */ - public void setPosition(final int newPos); + public abstract void setPosition(final int newPos); /** * Gets the position of the buffer. * * @return the position */ - public int getPosition(); + public abstract int getPosition(); /** * Reads and returns the PtNode count out of a buffer and forwards the pointer. */ - public int readPtNodeCount(); + public abstract int readPtNodeCount(); /** * Reads the forward link and advances the position. * - * @return if this method advances the position then true else false. + * @return true if this method moves the file pointer, false otherwise. */ - public boolean readForwardLinkAndAdvancePosition(); - public boolean hasNextPtNodeArray(); + public abstract boolean readAndFollowForwardLink(); + public abstract boolean hasNextPtNodeArray(); /** * Opens the dictionary file and makes DictBuffer. */ @UsedForTesting - public void openDictBuffer() throws FileNotFoundException, IOException; + public abstract void openDictBuffer() throws FileNotFoundException, IOException; @UsedForTesting - public boolean isOpenedDictBuffer(); + public abstract boolean isDictBufferOpen(); - // Flags for DictionaryBufferFactory. + // Constants for DictionaryBufferFactory. public static final int USE_READONLY_BYTEBUFFER = 0x01000000; public static final int USE_BYTEARRAY = 0x02000000; - public static final int USE_WRITABLE_BYTEBUFFER = 0x04000000; + public static final int USE_WRITABLE_BYTEBUFFER = 0x03000000; public static final int MASK_DICTBUFFER = 0x0F000000; public interface DictionaryBufferFactory { @@ -221,4 +271,124 @@ public interface DictDecoder { return null; } } + + /** + * A utility class for reading a file header. + */ + protected static class HeaderReader { + protected static int readVersion(final DictBuffer dictBuffer) + throws IOException, UnsupportedFormatException { + return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer); + } + + protected static int readOptionFlags(final DictBuffer dictBuffer) { + return dictBuffer.readUnsignedShort(); + } + + protected static int readHeaderSize(final DictBuffer dictBuffer) { + return dictBuffer.readInt(); + } + + protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer, + final int headerSize) { + final HashMap<String, String> attributes = new HashMap<String, String>(); + while (dictBuffer.position() < headerSize) { + // We can avoid an infinite loop here since dictBuffer.position() is always + // increased by calling CharEncoding.readString. + final String key = CharEncoding.readString(dictBuffer); + final String value = CharEncoding.readString(dictBuffer); + attributes.put(key, value); + } + dictBuffer.position(headerSize); + return attributes; + } + } + + /** + * A utility class for reading a PtNode. + */ + protected static class PtNodeReader { + protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { + return dictBuffer.readUnsignedByte(); + } + + protected static int readParentAddress(final DictBuffer dictBuffer, + final FormatOptions formatOptions) { + if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { + return BinaryDictDecoderUtils.readSInt24(dictBuffer); + } else { + return FormatSpec.NO_PARENT_ADDRESS; + } + } + + protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags, + final FormatOptions formatOptions) { + if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { + final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer); + if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; + return address; + } else { + switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: + return dictBuffer.readUnsignedByte(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: + return dictBuffer.readUnsignedShort(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: + return dictBuffer.readUnsignedInt24(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: + default: + return FormatSpec.NO_CHILDREN_ADDRESS; + } + } + } + + // Reads shortcuts and returns the read length. + protected static int readShortcut(final DictBuffer dictBuffer, + final ArrayList<WeightedString> shortcutTargets) { + final int pointerBefore = dictBuffer.position(); + dictBuffer.readUnsignedShort(); // skip the size + while (true) { + final int targetFlags = dictBuffer.readUnsignedByte(); + final String word = CharEncoding.readString(dictBuffer); + shortcutTargets.add(new WeightedString(word, + targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); + if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; + } + return dictBuffer.position() - pointerBefore; + } + + protected static int readBigramAddresses(final DictBuffer dictBuffer, + final ArrayList<PendingAttribute> bigrams, final int baseAddress) { + int readLength = 0; + int bigramCount = 0; + while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + final int bigramFlags = dictBuffer.readUnsignedByte(); + ++readLength; + final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) + ? 1 : -1; + int bigramAddress = baseAddress + readLength; + switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: + bigramAddress += sign * dictBuffer.readUnsignedByte(); + readLength += 1; + break; + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: + bigramAddress += sign * dictBuffer.readUnsignedShort(); + readLength += 2; + break; + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: + bigramAddress += sign * dictBuffer.readUnsignedInt24(); + readLength += 3; + break; + default: + throw new RuntimeException("Has bigrams with no address"); + } + bigrams.add(new PendingAttribute( + bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, + bigramAddress)); + if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; + } + return readLength; + } + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 96ccd8e49..51b89a02a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -360,18 +360,26 @@ public final class FormatSpec { * Returns new dictionary decoder. * * @param dictFile the dictionary file. - * @param bufferType the flag indicating buffer type which is used by the dictionary decoder. + * @param bufferType The type of buffer, as one of USE_* in DictDecoder. * @return new dictionary decoder if the dictionary file exists, otherwise null. */ public static DictDecoder getDictDecoder(final File dictFile, final int bufferType) { - if (!dictFile.isFile()) return null; - return new Ver3DictDecoder(dictFile, bufferType); + if (dictFile.isDirectory()) { + return new Ver4DictDecoder(dictFile, bufferType); + } else if (dictFile.isFile()) { + return new Ver3DictDecoder(dictFile, bufferType); + } + return null; } public static DictDecoder getDictDecoder(final File dictFile, final DictionaryBufferFactory factory) { - if (!dictFile.isFile()) return null; - return new Ver3DictDecoder(dictFile, factory); + if (dictFile.isDirectory()) { + return new Ver4DictDecoder(dictFile, factory); + } else if (dictFile.isFile()) { + return new Ver3DictDecoder(dictFile, factory); + } + return null; } public static DictDecoder getDictDecoder(final File dictFile) { diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java index 1a90a4b98..848277cd4 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java @@ -32,14 +32,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.TreeMap; /** * An implementation of DictDecoder for version 3 binary dictionary. */ @UsedForTesting -public class Ver3DictDecoder implements DictDecoder { +public class Ver3DictDecoder extends DictDecoder { private static final String TAG = Ver3DictDecoder.class.getSimpleName(); static { @@ -49,124 +47,10 @@ public class Ver3DictDecoder implements DictDecoder { // TODO: implement something sensical instead of just a phony method private static native int doNothing(); - private final static class HeaderReader { - protected static int readVersion(final DictBuffer dictBuffer) - throws IOException, UnsupportedFormatException { - return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer); - } - - protected static int readOptionFlags(final DictBuffer dictBuffer) { - return dictBuffer.readUnsignedShort(); - } - - protected static int readHeaderSize(final DictBuffer dictBuffer) { - return dictBuffer.readInt(); - } - - protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer, - final int headerSize) { - final HashMap<String, String> attributes = new HashMap<String, String>(); - while (dictBuffer.position() < headerSize) { - // We can avoid an infinite loop here since dictBuffer.position() is always - // increased by calling CharEncoding.readString. - final String key = CharEncoding.readString(dictBuffer); - final String value = CharEncoding.readString(dictBuffer); - attributes.put(key, value); - } - dictBuffer.position(headerSize); - return attributes; - } - } - - private final static class PtNodeReader { - protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { + protected static class PtNodeReader extends DictDecoder.PtNodeReader { + private static int readFrequency(final DictBuffer dictBuffer) { return dictBuffer.readUnsignedByte(); } - - protected static int readParentAddress(final DictBuffer dictBuffer, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - return BinaryDictDecoderUtils.readSInt24(dictBuffer); - } else { - return FormatSpec.NO_PARENT_ADDRESS; - } - } - - protected static int readFrequency(final DictBuffer dictBuffer) { - return dictBuffer.readUnsignedByte(); - } - - protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer); - if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; - return address; - } else { - switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: - return dictBuffer.readUnsignedByte(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: - return dictBuffer.readUnsignedShort(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: - return dictBuffer.readUnsignedInt24(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: - default: - return FormatSpec.NO_CHILDREN_ADDRESS; - } - } - } - - // Reads shortcuts and returns the read length. - protected static int readShortcut(final DictBuffer dictBuffer, - final ArrayList<WeightedString> shortcutTargets) { - final int pointerBefore = dictBuffer.position(); - dictBuffer.readUnsignedShort(); // skip the size - while (true) { - final int targetFlags = dictBuffer.readUnsignedByte(); - final String word = CharEncoding.readString(dictBuffer); - shortcutTargets.add(new WeightedString(word, - targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); - if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return dictBuffer.position() - pointerBefore; - } - - protected static int readBigrams(final DictBuffer dictBuffer, - final ArrayList<PendingAttribute> bigrams, final int baseAddress) { - int readLength = 0; - int bigramCount = 0; - while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - final int bigramFlags = dictBuffer.readUnsignedByte(); - ++readLength; - final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) - ? 1 : -1; - int bigramAddress = baseAddress + readLength; - switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: - bigramAddress += sign * dictBuffer.readUnsignedByte(); - readLength += 1; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: - bigramAddress += sign * dictBuffer.readUnsignedShort(); - readLength += 2; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: - final int offset = (dictBuffer.readUnsignedByte() << 16) - + dictBuffer.readUnsignedShort(); - bigramAddress += sign * offset; - readLength += 3; - break; - default: - throw new RuntimeException("Has bigrams with no address"); - } - bigrams.add(new PendingAttribute( - bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, - bigramAddress)); - if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return readLength; - } } private final File mDictionaryBinaryFile; @@ -199,7 +83,7 @@ public class Ver3DictDecoder implements DictDecoder { } @Override - public boolean isOpenedDictBuffer() { + public boolean isDictBufferOpen() { return mDictBuffer != null; } @@ -218,25 +102,11 @@ public class Ver3DictDecoder implements DictDecoder { if (mDictBuffer == null) { openDictBuffer(); } - - final int version = HeaderReader.readVersion(mDictBuffer); - final int optionsFlags = HeaderReader.readOptionFlags(mDictBuffer); - - final int headerSize = HeaderReader.readHeaderSize(mDictBuffer); - - if (headerSize < 0) { - throw new UnsupportedFormatException("header size can't be negative."); + final FileHeader header = super.readHeader(mDictBuffer); + final int version = header.mFormatOptions.mVersion; + if (!(version >= 2 && version <= 3)) { + throw new UnsupportedFormatException("File header has a wrong version : " + version); } - - final HashMap<String, String> attributes = HeaderReader.readAttributes(mDictBuffer, - headerSize); - - final FileHeader header = new FileHeader(headerSize, - new FusionDictionary.DictionaryOptions(attributes, - 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), - 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), - new FormatOptions(version, - 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); return header; } @@ -246,11 +116,11 @@ public class Ver3DictDecoder implements DictDecoder { public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) { int addressPointer = ptNodePos; final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); - ++addressPointer; + addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options); if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { - addressPointer += 3; + addressPointer += FormatSpec.PARENT_ADDRESS_SIZE; } final int characters[]; @@ -258,7 +128,7 @@ public class Ver3DictDecoder implements DictDecoder { int index = 0; int character = CharEncoding.readChar(mDictBuffer); addressPointer += CharEncoding.getCharSize(character); - while (-1 != character) { + while (FormatSpec.INVALID_CHARACTER != character) { // FusionDictionary is making sure that the length of the word is smaller than // MAX_WORD_LENGTH. // So we'll never write past the end of mCharacterBuffer. @@ -274,8 +144,8 @@ public class Ver3DictDecoder implements DictDecoder { } final int frequency; if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { - ++addressPointer; frequency = PtNodeReader.readFrequency(mDictBuffer); + addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE; } else { frequency = PtNode.NOT_A_TERMINAL; } @@ -296,7 +166,8 @@ public class Ver3DictDecoder implements DictDecoder { final ArrayList<PendingAttribute> bigrams; if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { bigrams = new ArrayList<PendingAttribute>(); - addressPointer += PtNodeReader.readBigrams(mDictBuffer, bigrams, addressPointer); + addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, + addressPointer); if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { MakedictLog.d("too many bigrams in a PtNode."); } @@ -332,25 +203,6 @@ public class Ver3DictDecoder implements DictDecoder { } @Override - public int getTerminalPosition(String word) throws IOException, UnsupportedFormatException { - if (mDictBuffer == null) { - openDictBuffer(); - } - return BinaryDictIOUtils.getTerminalPosition(this, word); - } - - @Override - public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words, - final TreeMap<Integer, Integer> frequencies, - final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams) - throws IOException, UnsupportedFormatException { - if (mDictBuffer == null) { - openDictBuffer(); - } - BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams); - } - - @Override public void setPosition(int newPos) { mDictBuffer.position(newPos); } @@ -366,7 +218,7 @@ public class Ver3DictDecoder implements DictDecoder { } @Override - public boolean readForwardLinkAndAdvancePosition() { + public boolean readAndFollowForwardLink() { final int nextAddress = mDictBuffer.readUnsignedInt24(); if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) { mDictBuffer.position(nextAddress); diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java index 222a0f474..76f0f4052 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java @@ -167,7 +167,7 @@ public class Ver3DictEncoder implements DictEncoder { } } - public void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) { + private void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) { final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions); if (formatOptions.mSupportsDynamicUpdate) { mPosition += BinaryDictEncoderUtils.writeSignedChildrenPosition(mBuffer, mPosition, diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java new file mode 100644 index 000000000..36c5a2720 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -0,0 +1,259 @@ +/* + * 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.makedict; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +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 android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * An implementation of binary dictionary decoder for version 4 binary dictionary. + */ +@UsedForTesting +public class Ver4DictDecoder extends DictDecoder { + private static final String TAG = Ver4DictDecoder.class.getSimpleName(); + + private static final int FILETYPE_TRIE = 1; + private static final int FILETYPE_FREQUENCY = 2; + + private final File mDictDirectory; + private final DictionaryBufferFactory mBufferFactory; + private DictBuffer mDictBuffer; + private DictBuffer mFrequencyBuffer; + + @UsedForTesting + /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) { + mDictDirectory = dictDirectory; + mDictBuffer = mFrequencyBuffer = null; + + if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) { + mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); + } else if ((factoryFlag & MASK_DICTBUFFER) == USE_BYTEARRAY) { + mBufferFactory = new DictionaryBufferFromByteArrayFactory(); + } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) { + mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory(); + } else { + mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); + } + } + + @UsedForTesting + /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) { + mDictDirectory = dictDirectory; + mBufferFactory = factory; + mDictBuffer = mFrequencyBuffer = null; + } + + private File getFile(final int fileType) { + if (fileType == FILETYPE_TRIE) { + return new File(mDictDirectory, + mDictDirectory.getName() + FormatSpec.TRIE_FILE_EXTENSION); + } else if (fileType == FILETYPE_FREQUENCY) { + return new File(mDictDirectory, + mDictDirectory.getName() + FormatSpec.FREQ_FILE_EXTENSION); + } else { + throw new RuntimeException("Unsupported kind of file : " + fileType); + } + } + + @Override + public void openDictBuffer() throws FileNotFoundException, IOException { + final String filename = mDictDirectory.getName(); + mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE)); + mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY)); + } + + @Override + public boolean isDictBufferOpen() { + return mDictBuffer != null; + } + + /* package */ DictBuffer getDictBuffer() { + return mDictBuffer; + } + + @Override + public FileHeader readHeader() throws IOException, UnsupportedFormatException { + if (mDictBuffer == null) { + openDictBuffer(); + } + final FileHeader header = super.readHeader(mDictBuffer); + final int version = header.mFormatOptions.mVersion; + if (version != 4) { + throw new UnsupportedFormatException("File header has a wrong version : " + version); + } + return header; + } + + protected static class PtNodeReader extends DictDecoder.PtNodeReader { + protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId) { + frequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE + 1); + return frequencyBuffer.readUnsignedByte(); + } + + protected static int readTerminalId(final DictBuffer dictBuffer) { + return dictBuffer.readInt(); + } + } + + // TODO: Make this buffer thread safe. + // TODO: Support words longer than FormatSpec.MAX_WORD_LENGTH. + private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH]; + @Override + public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) { + int addressPointer = ptNodePos; + final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); + addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; + + final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options); + if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { + addressPointer += FormatSpec.PARENT_ADDRESS_SIZE; + } + + final int characters[]; + if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) { + int index = 0; + int character = CharEncoding.readChar(mDictBuffer); + addressPointer += CharEncoding.getCharSize(character); + while (FormatSpec.INVALID_CHARACTER != character + && index < FormatSpec.MAX_WORD_LENGTH) { + mCharacterBuffer[index++] = character; + character = CharEncoding.readChar(mDictBuffer); + addressPointer += CharEncoding.getCharSize(character); + } + characters = Arrays.copyOfRange(mCharacterBuffer, 0, index); + } else { + final int character = CharEncoding.readChar(mDictBuffer); + addressPointer += CharEncoding.getCharSize(character); + characters = new int[] { character }; + } + final int terminalId; + if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { + terminalId = PtNodeReader.readTerminalId(mDictBuffer); + addressPointer += FormatSpec.PTNODE_TERMINAL_ID_SIZE; + } else { + terminalId = PtNode.NOT_A_TERMINAL; + } + + final int frequency; + if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { + frequency = PtNodeReader.readFrequency(mFrequencyBuffer, terminalId); + } else { + frequency = PtNode.NOT_A_TERMINAL; + } + int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options); + if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { + childrenAddress += addressPointer; + } + addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options); + final ArrayList<WeightedString> shortcutTargets; + if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) { + // readShortcut will add shortcuts to shortcutTargets. + shortcutTargets = new ArrayList<WeightedString>(); + addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets); + } else { + shortcutTargets = null; + } + + final ArrayList<PendingAttribute> bigrams; + if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { + bigrams = new ArrayList<PendingAttribute>(); + addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, + addressPointer); + if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + MakedictLog.d("too many bigrams in a node."); + } + } else { + bigrams = null; + } + return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency, + parentAddress, childrenAddress, shortcutTargets, bigrams); + } + + private void deleteDictFiles() { + final File[] files = mDictDirectory.listFiles(); + for (int i = 0; i < files.length; ++i) { + files[i].delete(); + } + } + + @Override + public FusionDictionary readDictionaryBinary(final FusionDictionary dict, + final boolean deleteDictIfBroken) + throws FileNotFoundException, IOException, UnsupportedFormatException { + if (mDictBuffer == null) { + openDictBuffer(); + } + try { + return BinaryDictDecoderUtils.readDictionaryBinary(this, dict); + } catch (IOException e) { + Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e); + if (deleteDictIfBroken) { + deleteDictFiles(); + } + throw e; + } catch (UnsupportedFormatException e) { + Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e); + if (deleteDictIfBroken) { + deleteDictFiles(); + } + throw e; + } + } + + @Override + public void setPosition(int newPos) { + mDictBuffer.position(newPos); + } + + @Override + public int getPosition() { + return mDictBuffer.position(); + } + + @Override + public int readPtNodeCount() { + return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer); + } + + @Override + public boolean readAndFollowForwardLink() { + final int nextAddress = mDictBuffer.readUnsignedInt24(); + if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) { + mDictBuffer.position(nextAddress); + return true; + } + return false; + } + + @Override + public boolean hasNextPtNodeArray() { + return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS; + } +} |