diff options
81 files changed, 1618 insertions, 720 deletions
diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java index c22c5770f..9d7258de7 100644 --- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java +++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java @@ -45,4 +45,9 @@ public final class ProductionFlags { * When {@code false}, the split keyboard is not yet ready to be enabled. */ public static final boolean IS_SPLIT_KEYBOARD_SUPPORTED = true; + + /** + * When {@code false}, account sign-in in keyboard is not yet ready to be enabled. + */ + public static final boolean ENABLE_ACCOUNT_SIGN_IN = false; } diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java index 38735eccb..2274852ce 100644 --- a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java @@ -20,6 +20,8 @@ import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.settings.SettingsValues; +import javax.annotation.Nullable; + public final class StatsUtils { private StatsUtils() { @@ -63,4 +65,8 @@ public final class StatsUtils { public static void onStartInputView(int inputType, int displayOrientation, boolean restarting) { } + + public static void onAutoCorrection(final String typedWord, final String autoCorrectionWord, + final boolean isBatchInput, @Nullable final String dictionaryType) { + } } diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index a1ffe5a93..054c415a4 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -18,7 +18,7 @@ coreApp="true" package="com.android.inputmethod.latin"> - <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index f1253b40c..8ee859bc7 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -483,6 +483,8 @@ <attr name="localeCode" format="string" /> <attr name="languageCode" format="string" /> <attr name="countryCode" format="string" /> + <!-- Enable split keyboard layout. Disabled by default. --> + <attr name="isSplitLayout" format="boolean" /> </declare-styleable> <declare-styleable name="Keyboard_KeyStyle"> @@ -514,6 +516,8 @@ <attr name="elementKeyboard" format="reference"/> <!-- Enable proximity characters correction. Disabled by default. --> <attr name="enableProximityCharsCorrection" format="boolean" /> + <!-- Indicates if the keyboard layout supports being split or not. false by default --> + <attr name="supportsSplitLayout" format="boolean" /> </declare-styleable> <declare-styleable name="KeyboardLayoutSet_Feature"> diff --git a/java/res/values/keyboard-themes.xml b/java/res/values/keyboard-themes.xml index 9d772c4e7..b0bae9647 100644 --- a/java/res/values/keyboard-themes.xml +++ b/java/res/values/keyboard-themes.xml @@ -26,10 +26,10 @@ <item>@string/keyboard_theme_holo_blue</item> </string-array> <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. --> - <string-array name="keyboard_theme_ids" translatable="false"> + <integer-array name="keyboard_theme_ids" translatable="false"> <item>3</item> <item>4</item> <item>2</item> <item>0</item> - </string-array> + </integer-array> </resources> diff --git a/java/res/xml-sw600dp-land/key_space_3kw.xml b/java/res/xml-sw600dp-land/key_space_3kw.xml new file mode 100644 index 000000000..47c4e4809 --- /dev/null +++ b/java/res/xml-sw600dp-land/key_space_3kw.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, 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" +> + <!-- TODO: Consolidate the layout specification between protrait and landscape. + Ideally just the keyWidth should be different --> + <switch> + <!-- fa: Perisan + kn: Kannada + ne: Nepali + te: Telugu --> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="false" + > + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="14.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="14.0%p" /> + </case> + <!-- languageSwitchKeyEnabled="false" --> + <default> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="21.0%p" /> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp-land/row_qwerty4.xml b/java/res/xml-sw600dp-land/row_qwerty4.xml new file mode 100644 index 000000000..0fdb5c6f5 --- /dev/null +++ b/java/res/xml-sw600dp-land/row_qwerty4.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, 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> + <!-- Split the 4th row for split layouts --> + <case + latin:isSplitLayout="true" + > + <Row + latin:keyWidth="7.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyboardLayout="@xml/key_space_3kw" + latin:backgroundType="normal" /> + <Spacer + latin:keyWidth="28.0%p" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="21.0%p" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </case> + <default> + <Row + latin:keyWidth="9.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyXPos="19.0%p" + latin:keyboardLayout="@xml/key_space_7kw" + latin:backgroundType="normal" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp-land/rows_qwerty.xml b/java/res/xml-sw600dp-land/rows_qwerty.xml new file mode 100644 index 000000000..b580dcf6a --- /dev/null +++ b/java/res/xml-sw600dp-land/rows_qwerty.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <!-- First row --> + <Row> + <switch> + <!-- Split keyboard layout for the first row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_left5" + latin:keyWidth="7.0%p" /> + <Spacer + latin:keyWidth="20.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_right5" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the first row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> + </Row> + <!-- Second row --> + <Row> + <switch> + <!-- Split keyboard layout for the second row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_left5" + latin:keyXPos="4.0%p" + latin:keyWidth="7.0%p" /> + <Spacer + latin:keyWidth="23.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_right4" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the second row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2" + latin:keyXPos="4.5%p" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> + </Row> + <!-- Third row --> + <Row> + <switch> + <!-- Split keyboard layout for the third row --> + <case + latin:isSplitLayout="true" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_left4" + latin:keyWidth="7.0%p" /> + <Spacer + latin:keyWidth="17.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_right3" + latin:keyWidth="7.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the third row --> + <default> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3" + latin:keyWidth="9.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> + </Row> + <!-- Fourth row --> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw600dp/key_space_3kw.xml b/java/res/xml-sw600dp/key_space_3kw.xml new file mode 100644 index 000000000..9932d342e --- /dev/null +++ b/java/res/xml-sw600dp/key_space_3kw.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, 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> + <!-- fa: Perisan + kn: Kannada + ne: Nepali + te: Telugu --> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="false" + > + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="16.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="16.0%p" /> + </case> + <!-- languageSwitchKeyEnabled="false" --> + <default> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="24.0%p" /> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml index ed7150de4..bcfd2cb7f 100644 --- a/java/res/xml-sw600dp/row_qwerty4.xml +++ b/java/res/xml-sw600dp/row_qwerty4.xml @@ -21,23 +21,54 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Row - latin:keyWidth="9.0%p" - latin:backgroundType="functional" - > - <Key - latin:keyStyle="toSymbolKeyStyle" - latin:keyWidth="10.0%p" /> - <include - latin:keyboardLayout="@xml/key_comma" /> - <!-- Space key. --> - <include - latin:keyXPos="19.0%p" - latin:keyboardLayout="@xml/key_space_7kw" - latin:backgroundType="normal" /> - <include - latin:keyboardLayout="@xml/key_period" /> - <include - latin:keyboardLayout="@xml/key_emoji" /> - </Row> + <switch> + <!-- Split the 4th row for split layouts --> + <case + latin:isSplitLayout="true" + > + <Row + latin:keyWidth="8.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyboardLayout="@xml/key_space_3kw" + latin:backgroundType="normal" /> + <Spacer + latin:keyWidth="20.0%p" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="24.0%p" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </case> + <default> + <Row + latin:keyWidth="9.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyXPos="19.0%p" + latin:keyboardLayout="@xml/key_space_7kw" + latin:backgroundType="normal" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </default> + </switch> </merge> diff --git a/java/res/xml-sw600dp/rows_qwerty.xml b/java/res/xml-sw600dp/rows_qwerty.xml index 58ba1d713..51df4b0cc 100644 --- a/java/res/xml-sw600dp/rows_qwerty.xml +++ b/java/res/xml-sw600dp/rows_qwerty.xml @@ -23,39 +23,114 @@ > <include latin:keyboardLayout="@xml/key_styles_common" /> - <Row - latin:keyWidth="9.0%p" - > - <include - latin:keyboardLayout="@xml/rowkeys_qwerty1" /> - <Key - latin:keyStyle="deleteKeyStyle" - latin:keyWidth="fillRight" /> + <!-- TODO: Consolidate the layout specification between protrait and landscape. + Ideally just the keyWidth should be different and the spacer should adjust to fill + the available space. --> + <!-- First row --> + <Row> + <switch> + <!-- Split keyboard layout for the first row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_left5" + latin:keyWidth="8.0%p" /> + <Spacer + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_right5" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the first row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> </Row> - <Row - latin:keyWidth="9.0%p" - > - <include - latin:keyboardLayout="@xml/rowkeys_qwerty2" - latin:keyXPos="4.5%p" /> - <Key - latin:keyStyle="enterKeyStyle" - latin:keyWidth="fillRight" /> + <!-- Second row --> + <Row> + <switch> + <!-- Split keyboard layout for the second row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_left5" + latin:keyXPos="4.0%p" + latin:keyWidth="8.0%p" /> + <Spacer + latin:keyWidth="14.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_right4" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the second row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2" + latin:keyXPos="4.5%p" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> </Row> - <Row - latin:keyWidth="9.0%p" - > - <Key - latin:keyStyle="shiftKeyStyle" - latin:keyWidth="10.0%p" /> - <include - latin:keyboardLayout="@xml/rowkeys_qwerty3" /> - <include - latin:keyboardLayout="@xml/keys_exclamation_question" /> - <Key - latin:keyStyle="shiftKeyStyle" - latin:keyWidth="fillRight" /> + <!-- Third row --> + <Row> + <switch> + <!-- Split keyboard layout for the third row --> + <case + latin:isSplitLayout="true" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_left4" + latin:keyWidth="8.0%p" /> + <Spacer + latin:keyWidth="8.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_right3" + latin:keyWidth="8.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the third row --> + <default> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3" + latin:keyWidth="9.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> </Row> + <!-- Fourth row --> <include latin:keyboardLayout="@xml/row_qwerty4" /> </merge> diff --git a/java/res/xml/keyboard_layout_set_qwerty.xml b/java/res/xml/keyboard_layout_set_qwerty.xml index 821517081..1aa6f010a 100644 --- a/java/res/xml/keyboard_layout_set_qwerty.xml +++ b/java/res/xml/keyboard_layout_set_qwerty.xml @@ -23,7 +23,8 @@ <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_qwerty" - latin:enableProximityCharsCorrection="true" /> + latin:enableProximityCharsCorrection="true" + latin:supportsSplitLayout="false" /> <Element latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> diff --git a/java/res/xml/rowkeys_qwerty1.xml b/java/res/xml/rowkeys_qwerty1.xml index 8f3b160fe..b8e4a4c78 100644 --- a/java/res/xml/rowkeys_qwerty1.xml +++ b/java/res/xml/rowkeys_qwerty1.xml @@ -21,53 +21,10 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="!text/keyspec_q" - latin:keyHintLabel="1" - latin:additionalMoreKeys="1" - latin:moreKeys="!text/morekeys_q" /> - <Key - latin:keySpec="!text/keyspec_w" - latin:keyHintLabel="2" - latin:additionalMoreKeys="2" - latin:moreKeys="!text/morekeys_w" /> - <Key - latin:keySpec="e" - latin:keyHintLabel="3" - latin:additionalMoreKeys="3" - latin:moreKeys="!text/morekeys_e" /> - <Key - latin:keySpec="r" - latin:keyHintLabel="4" - latin:additionalMoreKeys="4" - latin:moreKeys="!text/morekeys_r" /> - <Key - latin:keySpec="t" - latin:keyHintLabel="5" - latin:additionalMoreKeys="5" - latin:moreKeys="!text/morekeys_t" /> - <Key - latin:keySpec="!text/keyspec_y" - latin:keyHintLabel="6" - latin:additionalMoreKeys="6" - latin:moreKeys="!text/morekeys_y" /> - <Key - latin:keySpec="u" - latin:keyHintLabel="7" - latin:additionalMoreKeys="7" - latin:moreKeys="!text/morekeys_u" /> - <Key - latin:keySpec="i" - latin:keyHintLabel="8" - latin:additionalMoreKeys="8" - latin:moreKeys="!text/morekeys_i" /> - <Key - latin:keySpec="o" - latin:keyHintLabel="9" - latin:additionalMoreKeys="9" - latin:moreKeys="!text/morekeys_o" /> - <Key - latin:keySpec="p" - latin:keyHintLabel="0" - latin:additionalMoreKeys="0" /> + <!-- q,w,e,r,t --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_left5" /> + <!-- y,u,i,o,p --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_right5" /> </merge> diff --git a/java/res/xml/rowkeys_qwerty1_left5.xml b/java/res/xml/rowkeys_qwerty1_left5.xml new file mode 100644 index 000000000..ff9f1b2b5 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty1_left5.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="!text/keyspec_q" + latin:keyHintLabel="1" + latin:additionalMoreKeys="1" + latin:moreKeys="!text/morekeys_q" /> + <Key + latin:keySpec="!text/keyspec_w" + latin:keyHintLabel="2" + latin:additionalMoreKeys="2" + latin:moreKeys="!text/morekeys_w" /> + <Key + latin:keySpec="e" + latin:keyHintLabel="3" + latin:additionalMoreKeys="3" + latin:moreKeys="!text/morekeys_e" /> + <Key + latin:keySpec="r" + latin:keyHintLabel="4" + latin:additionalMoreKeys="4" + latin:moreKeys="!text/morekeys_r" /> + <Key + latin:keySpec="t" + latin:keyHintLabel="5" + latin:additionalMoreKeys="5" + latin:moreKeys="!text/morekeys_t" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty1_right5.xml b/java/res/xml/rowkeys_qwerty1_right5.xml new file mode 100644 index 000000000..2b3cae2e8 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty1_right5.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="!text/keyspec_y" + latin:keyHintLabel="6" + latin:additionalMoreKeys="6" + latin:moreKeys="!text/morekeys_y" /> + <Key + latin:keySpec="u" + latin:keyHintLabel="7" + latin:additionalMoreKeys="7" + latin:moreKeys="!text/morekeys_u" /> + <Key + latin:keySpec="i" + latin:keyHintLabel="8" + latin:additionalMoreKeys="8" + latin:moreKeys="!text/morekeys_i" /> + <Key + latin:keySpec="o" + latin:keyHintLabel="9" + latin:additionalMoreKeys="9" + latin:moreKeys="!text/morekeys_o" /> + <Key + latin:keySpec="p" + latin:keyHintLabel="0" + latin:additionalMoreKeys="0" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty2.xml b/java/res/xml/rowkeys_qwerty2.xml index 4077beaf6..550db3b3f 100644 --- a/java/res/xml/rowkeys_qwerty2.xml +++ b/java/res/xml/rowkeys_qwerty2.xml @@ -21,30 +21,10 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="a" - latin:moreKeys="!text/morekeys_a" /> - <Key - latin:keySpec="s" - latin:moreKeys="!text/morekeys_s" /> - <Key - latin:keySpec="d" - latin:moreKeys="!text/morekeys_d" /> - <Key - latin:keySpec="f" /> - <Key - latin:keySpec="g" - latin:moreKeys="!text/morekeys_g" /> - <Key - latin:keySpec="h" - latin:moreKeys="!text/morekeys_h" /> - <Key - latin:keySpec="j" - latin:moreKeys="!text/morekeys_j" /> - <Key - latin:keySpec="k" - latin:moreKeys="!text/morekeys_k" /> - <Key - latin:keySpec="l" - latin:moreKeys="!text/morekeys_l" /> + <!-- a,s,d,f,g --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_left5" /> + <!-- h,j,k,l --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_right4" /> </merge> diff --git a/java/res/xml/rowkeys_qwerty2_left5.xml b/java/res/xml/rowkeys_qwerty2_left5.xml new file mode 100644 index 000000000..1803bf203 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty2_left5.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="a" + latin:moreKeys="!text/morekeys_a" /> + <Key + latin:keySpec="s" + latin:moreKeys="!text/morekeys_s" /> + <Key + latin:keySpec="d" + latin:moreKeys="!text/morekeys_d" /> + <Key + latin:keySpec="f" /> + <Key + latin:keySpec="g" + latin:moreKeys="!text/morekeys_g" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty2_right4.xml b/java/res/xml/rowkeys_qwerty2_right4.xml new file mode 100644 index 000000000..99936b7a2 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty2_right4.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="h" + latin:moreKeys="!text/morekeys_h" /> + <Key + latin:keySpec="j" + latin:moreKeys="!text/morekeys_j" /> + <Key + latin:keySpec="k" + latin:moreKeys="!text/morekeys_k" /> + <Key + latin:keySpec="l" + latin:moreKeys="!text/morekeys_l" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty3.xml b/java/res/xml/rowkeys_qwerty3.xml index 8562003d2..7a523f1f6 100644 --- a/java/res/xml/rowkeys_qwerty3.xml +++ b/java/res/xml/rowkeys_qwerty3.xml @@ -21,23 +21,10 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="z" - latin:moreKeys="!text/morekeys_z" /> - <Key - latin:keySpec="!text/keyspec_x" - latin:moreKeys="!text/morekeys_x" /> - <Key - latin:keySpec="c" - latin:moreKeys="!text/morekeys_c" /> - <Key - latin:keySpec="v" - latin:moreKeys="!text/morekeys_v" /> - <Key - latin:keySpec="b" /> - <Key - latin:keySpec="n" - latin:moreKeys="!text/morekeys_n" /> - <Key - latin:keySpec="m" /> + <!-- z,x,c,v --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_left4" /> + <!-- b,n,m --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_right3" /> </merge> diff --git a/java/res/xml/rowkeys_qwerty3_left4.xml b/java/res/xml/rowkeys_qwerty3_left4.xml new file mode 100644 index 000000000..6043c3bba --- /dev/null +++ b/java/res/xml/rowkeys_qwerty3_left4.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="z" + latin:moreKeys="!text/morekeys_z" /> + <Key + latin:keySpec="!text/keyspec_x" + latin:moreKeys="!text/morekeys_x" /> + <Key + latin:keySpec="c" + latin:moreKeys="!text/morekeys_c" /> + <Key + latin:keySpec="v" + latin:moreKeys="!text/morekeys_v" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty3_right3.xml b/java/res/xml/rowkeys_qwerty3_right3.xml new file mode 100644 index 000000000..f69910344 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty3_right3.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="b" /> + <Key + latin:keySpec="n" + latin:moreKeys="!text/morekeys_n" /> + <Key + latin:keySpec="m" /> +</merge> diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index bd1c1479a..99a34bec7 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -702,6 +702,10 @@ public class Key implements Comparable<Key> { return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO) != 0; } + public final boolean hasCustomActionLabel() { + return (mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0; + } + private final boolean isShiftedLetterActivated() { return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0 && !TextUtils.isEmpty(mHintLabel); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 538e515bc..43c61443e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -73,10 +73,12 @@ public final class KeyboardId { public final boolean mLanguageSwitchKeyEnabled; public final String mCustomActionLabel; public final boolean mHasShortcutKey; + public final boolean mIsSplitLayout; private final int mHashCode; - public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) { + public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params, + boolean isSplitLayout) { mSubtype = params.mSubtype; mLocale = SubtypeLocaleUtils.getSubtypeLocale(mSubtype); mWidth = params.mKeyboardWidth; @@ -89,6 +91,7 @@ public final class KeyboardId { mCustomActionLabel = (mEditorInfo.actionLabel != null) ? mEditorInfo.actionLabel.toString() : null; mHasShortcutKey = params.mVoiceInputKeyEnabled; + mIsSplitLayout = isSplitLayout; mHashCode = computeHashCode(this); } @@ -108,7 +111,8 @@ public final class KeyboardId { id.mCustomActionLabel, id.navigateNext(), id.navigatePrevious(), - id.mSubtype + id.mSubtype, + id.mIsSplitLayout }); } @@ -128,7 +132,8 @@ public final class KeyboardId { && TextUtils.equals(other.mCustomActionLabel, mCustomActionLabel) && other.navigateNext() == navigateNext() && other.navigatePrevious() == navigatePrevious() - && other.mSubtype.equals(mSubtype); + && other.mSubtype.equals(mSubtype) + && other.mIsSplitLayout == mIsSplitLayout; } private static boolean isAlphabetKeyboard(final int elementId) { @@ -175,7 +180,7 @@ public final class KeyboardId { @Override public String toString() { - return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s]", + return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]", elementIdToName(mElementId), mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), mWidth, mHeight, @@ -187,7 +192,8 @@ public final class KeyboardId { (passwordInput() ? " passwordInput" : ""), (mHasShortcutKey ? " hasShortcutKey" : ""), (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""), - (isMultiLine() ? " isMultiLine" : "") + (isMultiLine() ? " isMultiLine" : ""), + (mIsSplitLayout ? " isSplitLayout" : "") ); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 3f4367313..1dbecdc81 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -96,6 +96,7 @@ public final class KeyboardLayoutSet { private static final class ElementParams { int mKeyboardXmlId; boolean mProximityCharsCorrectionEnabled; + boolean mSupportsSplitLayout; public ElementParams() {} } @@ -168,7 +169,10 @@ public final class KeyboardLayoutSet { // attribute in a keyboard_layout_set XML file. Also each keyboard layout XML resource is // specified as an elementKeyboard attribute in the file. // The KeyboardId is an internal key for a Keyboard object. - final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams); + + // TODO: AND mSupportsSplitLayout with the user preference that forces a split. + final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams, + elementParams.mSupportsSplitLayout); try { return getKeyboard(elementParams, id); } catch (final RuntimeException e) { @@ -376,6 +380,8 @@ public final class KeyboardLayoutSet { elementParams.mProximityCharsCorrectionEnabled = a.getBoolean( R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection, false); + elementParams.mSupportsSplitLayout = a.getBoolean( + R.styleable.KeyboardLayoutSet_Element_supportsSplitLayout, false); mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams); } finally { a.recycle(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index d36c199e2..6cd7955ce 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -17,9 +17,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; -import android.preference.PreferenceManager; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -47,7 +45,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); private SubtypeSwitcher mSubtypeSwitcher; - private SharedPreferences mPrefs; private InputView mCurrentInputView; private View mMainKeyboardFrame; @@ -76,13 +73,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } public static void init(final LatinIME latinIme) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme); - sInstance.initInternal(latinIme, prefs); + sInstance.initInternal(latinIme); } - private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) { + private void initInternal(final LatinIME latinIme) { mLatinIME = latinIme; - mPrefs = prefs; mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mState = new KeyboardState(this); mIsHardwareAcceleratedDrawingEnabled = @@ -91,7 +86,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { public void updateKeyboardTheme() { final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper( - mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); + mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */)); if (themeUpdated && mKeyboardView != null) { mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled)); } @@ -348,7 +343,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } updateKeyboardThemeAndContextThemeWrapper( - mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); + mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */)); mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( R.layout.input_view, null); mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 7161d3f26..6d8c8b76f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -16,14 +16,17 @@ package com.android.inputmethod.keyboard; +import android.content.Context; import android.content.SharedPreferences; import android.os.Build.VERSION_CODES; +import android.preference.PreferenceManager; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.R; +import java.util.ArrayList; import java.util.Arrays; public final class KeyboardTheme implements Comparable<KeyboardTheme> { @@ -40,7 +43,10 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { public static final int THEME_ID_LXX_DARK = 4; public static final int DEFAULT_THEME_ID = THEME_ID_KLP; - private static final KeyboardTheme[] KEYBOARD_THEMES = { + private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES; + + @UsedForTesting + static final KeyboardTheme[] KEYBOARD_THEMES = { new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS, // This has never been selected because we support ICS or later. VERSION_CODES.BASE), @@ -93,9 +99,10 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } @UsedForTesting - static KeyboardTheme searchKeyboardThemeById(final int themeId) { + static KeyboardTheme searchKeyboardThemeById(final int themeId, + final KeyboardTheme[] availableThemeIds) { // TODO: This search algorithm isn't optimal if there are many themes. - for (final KeyboardTheme theme : KEYBOARD_THEMES) { + for (final KeyboardTheme theme : availableThemeIds) { if (theme.mThemeId == themeId) { return theme; } @@ -105,13 +112,14 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { @UsedForTesting static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, - final int sdkVersion) { + final int sdkVersion, final KeyboardTheme[] availableThemeArray) { final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null); if (klpThemeIdString != null) { if (sdkVersion <= VERSION_CODES.KITKAT) { try { final int themeId = Integer.parseInt(klpThemeIdString); - final KeyboardTheme theme = searchKeyboardThemeById(themeId); + final KeyboardTheme theme = searchKeyboardThemeById(themeId, + availableThemeArray); if (theme != null) { return theme; } @@ -125,22 +133,21 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { prefs.edit().remove(KLP_KEYBOARD_THEME_KEY).apply(); } // TODO: This search algorithm isn't optimal if there are many themes. - for (final KeyboardTheme theme : KEYBOARD_THEMES) { + for (final KeyboardTheme theme : availableThemeArray) { if (sdkVersion >= theme.mMinApiVersion) { return theme; } } - return searchKeyboardThemeById(DEFAULT_THEME_ID); + return searchKeyboardThemeById(DEFAULT_THEME_ID, availableThemeArray); } public static String getKeyboardThemeName(final int themeId) { - final KeyboardTheme theme = searchKeyboardThemeById(themeId); + final KeyboardTheme theme = searchKeyboardThemeById(themeId, KEYBOARD_THEMES); return theme.mThemeName; } - public static void saveKeyboardThemeId(final String themeIdString, - final SharedPreferences prefs) { - saveKeyboardThemeId(themeIdString, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); + public static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs) { + saveKeyboardThemeId(themeId, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } @UsedForTesting @@ -152,25 +159,45 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } @UsedForTesting - static void saveKeyboardThemeId(final String themeIdString, - final SharedPreferences prefs, final int sdkVersion) { + static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs, + final int sdkVersion) { final String prefKey = getPreferenceKey(sdkVersion); - prefs.edit().putString(prefKey, themeIdString).apply(); + prefs.edit().putString(prefKey, Integer.toString(themeId)).apply(); } - public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) { - return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); + public static KeyboardTheme getKeyboardTheme(final Context context) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + final KeyboardTheme[] availableThemeArray = getAvailableThemeArray(context); + return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray); + } + + static KeyboardTheme[] getAvailableThemeArray(final Context context) { + if (AVAILABLE_KEYBOARD_THEMES == null) { + final int[] availableThemeIdStringArray = context.getResources().getIntArray( + R.array.keyboard_theme_ids); + final ArrayList<KeyboardTheme> availableThemeList = new ArrayList<>(); + for (final int id : availableThemeIdStringArray) { + final KeyboardTheme theme = searchKeyboardThemeById(id, KEYBOARD_THEMES); + if (theme != null) { + availableThemeList.add(theme); + } + } + AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray( + new KeyboardTheme[availableThemeList.size()]); + } + return AVAILABLE_KEYBOARD_THEMES; } @UsedForTesting - static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { + static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion, + final KeyboardTheme[] availableThemeArray) { final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null); if (lxxThemeIdString == null) { - return getDefaultKeyboardTheme(prefs, sdkVersion); + return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray); } try { final int themeId = Integer.parseInt(lxxThemeIdString); - final KeyboardTheme theme = searchKeyboardThemeById(themeId); + final KeyboardTheme theme = searchKeyboardThemeById(themeId, availableThemeArray); if (theme != null) { return theme; } @@ -180,6 +207,6 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } // Remove preference that contains unknown or illegal theme id. prefs.edit().remove(LXX_KEYBOARD_THEME_KEY).apply(); - return getDefaultKeyboardTheme(prefs, sdkVersion); + return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray); } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index bb3cbb0eb..98cd1da54 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -343,7 +343,9 @@ public class KeyboardView extends View { final int keyWidth = key.getDrawWidth(); final int keyHeight = key.getHeight(); final int bgWidth, bgHeight, bgX, bgY; - if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)) { + if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags) + // HACK: To disable expanding normal/functional key background. + && !key.hasCustomActionLabel()) { final int intrinsicWidth = background.getIntrinsicWidth(); final int intrinsicHeight = background.getIntrinsicHeight(); final float minScale = Math.min( diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index c20546607..eced45ea5 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -61,9 +61,9 @@ public class DictionaryFacilitator { // dictionary. private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; - private DictionaryGroup mDictionaryGroup = new DictionaryGroup(); + private DictionaryGroup[] mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() }; private boolean mIsUserDictEnabled = false; - private volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); + private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0); // To synchronize assigning mDictionaryGroup to ensure closing dictionaries. private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; @@ -193,8 +193,9 @@ public class DictionaryFacilitator { mPersonalizationHelper.setIsMonolingualUser(isMonolingualUser); } + // TODO: remove this, replace with version returning multiple locales public Locale getLocale() { - return mDictionaryGroup.mLocale; + return mDictionaryGroups[0].mLocale; } private static ExpandableBinaryDictionary getSubDict(final String dictType, @@ -226,6 +227,21 @@ public class DictionaryFacilitator { usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */); } + private DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup[] dictionaryGroups, + final Locale locale) { + for (int i = 0; i < dictionaryGroups.length; ++i) { + if (locale.equals(dictionaryGroups[i].mLocale)) { + return dictionaryGroups[i]; + } + } + return null; + } + + private DictionaryGroup getDictionaryGroupForActiveLanguage() { + // TODO: implement this + return mDictionaryGroups[0]; + } + public void resetDictionariesWithDictNamePrefix(final Context context, final Locale newLocaleToUse, final boolean useContactsDict, final boolean usePersonalizedDicts, @@ -252,7 +268,7 @@ public class DictionaryFacilitator { final ArrayList<String> dictsForLocale = new ArrayList<>(); existingDictsToCleanup.put(newLocale, dictsForLocale); final DictionaryGroup currentDictionaryGroupForLocale = - newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; + findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); if (null == currentDictionaryGroupForLocale) { continue; } @@ -266,10 +282,11 @@ public class DictionaryFacilitator { } } - final HashMap<Locale, DictionaryGroup> newDictionaryGroups = new HashMap<>(); - for (final Locale newLocale : newLocales) { + final DictionaryGroup[] newDictionaryGroups = new DictionaryGroup[newLocales.length]; + for (int i = 0; i < newLocales.length; ++i) { + final Locale newLocale = newLocales[i]; final DictionaryGroup dictionaryGroupForLocale = - newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; + findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); final ArrayList<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale); final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); @@ -297,30 +314,29 @@ public class DictionaryFacilitator { } subDicts.put(subDictType, subDict); } - newDictionaryGroups.put(newLocale, new DictionaryGroup(newLocale, mainDict, subDicts)); + newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, subDicts); } // Replace Dictionaries. - // TODO: use multiple locales. - final DictionaryGroup newDictionaryGroup = newDictionaryGroups.get(newLocaleToUse); - final DictionaryGroup oldDictionaryGroup; + final DictionaryGroup[] oldDictionaryGroups; synchronized (mLock) { - oldDictionaryGroup = mDictionaryGroup; - mDictionaryGroup = newDictionaryGroup; + oldDictionaryGroups = mDictionaryGroups; + mDictionaryGroups = newDictionaryGroups; mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context); - if (null == newDictionaryGroup.getDict(Dictionary.TYPE_MAIN)) { + if (hasAtLeastOneUninitializedMainDictionary()) { asyncReloadUninitializedMainDictionaries(context, newLocales, listener); } } if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); + listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary()); } // Clean up old dictionaries. for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) { final ArrayList<String> dictTypesToCleanUp = existingDictsToCleanup.get(localeToCleanUp); - final DictionaryGroup dictionarySetToCleanup = oldDictionaryGroup; + final DictionaryGroup dictionarySetToCleanup = + findDictionaryGroupWithLocale(oldDictionaryGroups, localeToCleanUp); for (final String dictType : dictTypesToCleanUp) { dictionarySetToCleanup.closeDict(dictType); } @@ -330,12 +346,18 @@ public class DictionaryFacilitator { private void asyncReloadUninitializedMainDictionaries(final Context context, final Locale[] locales, final DictionaryInitializationListener listener) { final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); - mLatchForWaitingLoadingMainDictionary = latchForWaitingLoadingMainDictionary; + mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary; ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() { @Override public void run() { for (final Locale locale : locales) { - final DictionaryGroup dictionaryGroup = mDictionaryGroup; + final DictionaryGroup dictionaryGroup = + findDictionaryGroupWithLocale(mDictionaryGroups, locale); + if (null == dictionaryGroup) { + // This should never happen, but better safe than crashy + Log.w(TAG, "Expected a dictionary group for " + locale + " but none found"); + continue; + } final Dictionary mainDict = DictionaryFactory.createMainDictionaryFromManager(context, locale); synchronized (mLock) { @@ -348,7 +370,8 @@ public class DictionaryFacilitator { } } if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); + listener.onUpdateMainDictionaryAvailability( + hasAtLeastOneInitializedMainDictionary()); } latchForWaitingLoadingMainDictionary.countDown(); } @@ -381,17 +404,20 @@ public class DictionaryFacilitator { subDicts.put(dictType, dict); } } - mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, subDicts); + mDictionaryGroups = new DictionaryGroup[] { + new DictionaryGroup(locale, mainDictionary, subDicts) }; } public void closeDictionaries() { - final DictionaryGroup dictionaryGroup; + final DictionaryGroup[] dictionaryGroups; synchronized (mLock) { - dictionaryGroup = mDictionaryGroup; - mDictionaryGroup = new DictionaryGroup(); + dictionaryGroups = mDictionaryGroups; + mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() }; } - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - dictionaryGroup.closeDict(dictType); + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + dictionaryGroup.closeDict(dictType); + } } mDistracterFilter.close(); if (mPersonalizationHelper != null) { @@ -401,40 +427,71 @@ public class DictionaryFacilitator { @UsedForTesting public ExpandableBinaryDictionary getSubDictForTesting(final String dictName) { - return mDictionaryGroup.getSubDict(dictName); + return mDictionaryGroups[0].getSubDict(dictName); } - // The main dictionary could have been loaded asynchronously. Don't cache the return value - // of this method. - public boolean hasInitializedMainDictionary() { - final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); - return mainDict != null && mainDict.isInitialized(); + // The main dictionaries are loaded asynchronously. Don't cache the return value + // of these methods. + public boolean hasAtLeastOneInitializedMainDictionary() { + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final Dictionary mainDict = dictionaryGroup.getDict(Dictionary.TYPE_MAIN); + if (mainDict != null && mainDict.isInitialized()) { + return true; + } + } + return false; + } + + public boolean hasAtLeastOneUninitializedMainDictionary() { + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final Dictionary mainDict = dictionaryGroup.getDict(Dictionary.TYPE_MAIN); + if (mainDict == null || !mainDict.isInitialized()) { + return true; + } + } + return false; } public boolean hasPersonalizationDictionary() { - return mDictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + if (dictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION)) { + return true; + } + } + return false; } public void flushPersonalizationDictionary() { - final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = - mDictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); + final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion = + new HashSet<>(); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = + dictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); + personalizationDictsUsedForSuggestion.add(personalizationDictUsedForSuggestion); + } mPersonalizationHelper.flushPersonalizationDictionariesToUpdate( - personalizationDictUsedForSuggestion); + personalizationDictsUsedForSuggestion); mDistracterFilter.close(); } - public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit) + public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { - mLatchForWaitingLoadingMainDictionary.await(timeout, unit); + mLatchForWaitingLoadingMainDictionaries.await(timeout, unit); } @UsedForTesting public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit) throws InterruptedException { - waitForLoadingMainDictionary(timeout, unit); - final Map<String, ExpandableBinaryDictionary> dictMap = mDictionaryGroup.mSubDictMap; - for (final ExpandableBinaryDictionary dict : dictMap.values()) { - dict.waitAllTasksForTests(); + waitForLoadingMainDictionaries(timeout, unit); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final ExpandableBinaryDictionary dict : dictionaryGroup.mSubDictMap.values()) { + dict.waitAllTasksForTests(); + } } } @@ -453,7 +510,7 @@ public class DictionaryFacilitator { public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { - final DictionaryGroup dictionaryGroup = mDictionaryGroup; + final DictionaryGroup dictionaryGroup = getDictionaryGroupForActiveLanguage(); final String[] words = suggestion.split(Constants.WORD_SEPARATOR); PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo; for (int i = 0; i < words.length; i++) { @@ -520,7 +577,8 @@ public class DictionaryFacilitator { } private void removeWord(final String dictName, final String word) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); + final ExpandableBinaryDictionary dictionary = + getDictionaryGroupForActiveLanguage().getSubDict(dictName); if (dictionary != null) { dictionary.removeUnigramEntryDynamically(word); } @@ -536,20 +594,22 @@ public class DictionaryFacilitator { public SuggestionResults getSuggestionResults(final WordComposer composer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { - final DictionaryGroup dictionaryGroup = mDictionaryGroup; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - final Dictionary dictionary = dictionaryGroup.getDict(dictType); - if (null == dictionary) continue; - final ArrayList<SuggestedWordInfo> dictionarySuggestions = - dictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, - settingsValuesForSuggestion, sessionId, languageWeight); - if (null == dictionarySuggestions) continue; - suggestionResults.addAll(dictionarySuggestions); - if (null != suggestionResults.mRawSuggestions) { - suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaryGroup.getDict(dictType); + if (null == dictionary) continue; + final ArrayList<SuggestedWordInfo> dictionarySuggestions = + dictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, + settingsValuesForSuggestion, sessionId, languageWeight); + if (null == dictionarySuggestions) continue; + suggestionResults.addAll(dictionarySuggestions); + if (null != suggestionResults.mRawSuggestions) { + suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); + } } } return suggestionResults; @@ -559,20 +619,22 @@ public class DictionaryFacilitator { if (TextUtils.isEmpty(word)) { return false; } - final DictionaryGroup dictionaryGroup = mDictionaryGroup; - if (dictionaryGroup.mLocale == null) { - return false; - } - final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - final Dictionary dictionary = dictionaryGroup.getDict(dictType); - // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and - // would be immutable once it's finished initializing, but concretely a null test is - // probably good enough for the time being. - if (null == dictionary) continue; - if (dictionary.isValidWord(word) - || (ignoreCase && dictionary.isValidWord(lowerCasedWord))) { - return true; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + if (dictionaryGroup.mLocale == null) { + continue; + } + final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaryGroup.getDict(dictType); + // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and + // would be immutable once it's finished initializing, but concretely a null test is + // probably good enough for the time being. + if (null == dictionary) continue; + if (dictionary.isValidWord(word) + || (ignoreCase && dictionary.isValidWord(lowerCasedWord))) { + return true; + } } } return false; @@ -584,18 +646,20 @@ public class DictionaryFacilitator { return Dictionary.NOT_A_PROBABILITY; } int maxFreq = Dictionary.NOT_A_PROBABILITY; - final DictionaryGroup dictionaryGroup = mDictionaryGroup; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - final Dictionary dictionary = dictionaryGroup.getDict(dictType); - if (dictionary == null) continue; - final int tempFreq; - if (isGettingMaxFrequencyOfExactMatches) { - tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); - } else { - tempFreq = dictionary.getFrequency(word); - } - if (tempFreq >= maxFreq) { - maxFreq = tempFreq; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaryGroup.getDict(dictType); + if (dictionary == null) continue; + final int tempFreq; + if (isGettingMaxFrequencyOfExactMatches) { + tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); + } else { + tempFreq = dictionary.getFrequency(word); + } + if (tempFreq >= maxFreq) { + maxFreq = tempFreq; + } } } return maxFreq; @@ -610,9 +674,12 @@ public class DictionaryFacilitator { } private void clearSubDictionary(final String dictName) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); - if (dictionary != null) { - dictionary.clear(); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictName); + if (dictionary != null) { + dictionary.clear(); + } } } @@ -641,8 +708,10 @@ public class DictionaryFacilitator { public void addPhraseToContextualDictionary(final String[] phrase, final int probability, final int bigramProbabilityForWords, final int bigramProbabilityForPhrases) { + // TODO: we're inserting the phrase into the dictionary for the active language. Rethink + // this a bit from a theoretical point of view. final ExpandableBinaryDictionary contextualDict = - mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTEXTUAL); + getDictionaryGroupForActiveLanguage().getSubDict(Dictionary.TYPE_CONTEXTUAL); if (contextualDict == null) { return; } @@ -675,22 +744,27 @@ public class DictionaryFacilitator { } public void dumpDictionaryForDebug(final String dictName) { - final ExpandableBinaryDictionary dictToDump = mDictionaryGroup.getSubDict(dictName); - if (dictToDump == null) { - Log.e(TAG, "Cannot dump " + dictName + ". " - + "The dictionary is not being used for suggestion or cannot be dumped."); - return; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final ExpandableBinaryDictionary dictToDump = dictionaryGroup.getSubDict(dictName); + if (dictToDump == null) { + Log.e(TAG, "Cannot dump " + dictName + ". " + + "The dictionary is not being used for suggestion or cannot be dumped."); + return; + } + dictToDump.dumpAllWordsForDebug(); } - dictToDump.dumpAllWordsForDebug(); } public ArrayList<Pair<String, DictionaryStats>> getStatsOfEnabledSubDicts() { final ArrayList<Pair<String, DictionaryStats>> statsOfEnabledSubDicts = new ArrayList<>(); - final DictionaryGroup dictionaryGroup = mDictionaryGroup; - for (final String dictType : SUB_DICT_TYPES) { - final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); - if (dictionary == null) continue; - statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : SUB_DICT_TYPES) { + final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); + if (dictionary == null) continue; + statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); + } } return statsOfEnabledSubDicts; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java index fa0265d86..ff4a6bde1 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java @@ -84,7 +84,7 @@ public class DictionaryFacilitatorLruCache { private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) { for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) { try { - dictionaryFacilitator.waitForLoadingMainDictionary( + dictionaryFacilitator.waitForLoadingMainDictionaries( WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS); return; } catch (final InterruptedException e) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index a1dd67f27..671ba6714 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -156,23 +156,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } private void asyncExecuteTaskWithWriteLock(final Runnable task) { - asyncExecuteTaskWithLock(mLock.writeLock(), task); + asyncExecuteTaskWithLock(mLock.writeLock(), mDictName /* executorName */, task); } - private void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) { - asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, task); + private void asyncExecuteTaskWithLock(final Lock lock, final String executorName, + final Runnable task) { + asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task); } private void asyncPreCheckAndExecuteTaskWithWriteLock( final Callable<Boolean> preCheckTask, final Runnable task) { - asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask, task); + asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask, + mDictName /* executorName */, task); } // Execute task with lock when the result of preCheckTask is true or preCheckTask is null. private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, - final Callable<Boolean> preCheckTask, final Runnable task) { - ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { + final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) { + ExecutorUtils.getExecutor(executorName).execute(new Runnable() { @Override public void run() { if (preCheckTask != null) { @@ -676,10 +678,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); - asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() { + asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() { @Override public void run() { - Log.d(TAG, "Dump dictionary: " + mDictName); + Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale); try { final DictionaryHeader header = mBinaryDictionary.getHeader(); Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0a64c4c26..475782042 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1000,7 +1000,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mainKeyboardView.setMainDictionaryAvailability( - mDictionaryFacilitator.hasInitializedMainDictionary()); + mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()); mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); mainKeyboardView.setSlidingKeyInputPreviewEnabled( diff --git a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java index 43cebdfa4..396d062f8 100644 --- a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java @@ -88,17 +88,17 @@ public class PersonalizationHelperForDictionaryFacilitator { /** * Flush personalization dictionaries to dictionary files. Close dictionaries after writing - * files except the dictionary that is used for generating suggestions. + * files except the dictionaries that is used for generating suggestions. * - * @param personalizationDictUsedForSuggestion the personalization dictionary used for + * @param personalizationDictsUsedForSuggestion the personalization dictionaries used for * generating suggestions that won't be closed. */ public void flushPersonalizationDictionariesToUpdate( - final ExpandableBinaryDictionary personalizationDictUsedForSuggestion) { + final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion) { for (final ExpandableBinaryDictionary personalizationDict : mPersonalizationDictsToUpdate.values()) { personalizationDict.asyncFlushBinaryDictionary(); - if (personalizationDict != personalizationDictUsedForSuggestion) { + if (!personalizationDictsUsedForSuggestion.contains(personalizationDict)) { // Close if the dictionary is not being used for suggestion. personalizationDict.close(); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 9e4aa40a2..4ad5ba65e 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -157,7 +157,7 @@ public final class Suggest { if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions || suggestionResults.isEmpty() || wordComposer.hasDigits() || wordComposer.isMostlyCaps() || wordComposer.isResumed() - || !mDictionaryFacilitator.hasInitializedMainDictionary() + || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary() || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { // If we don't have a main dictionary, we never want to auto-correct. The reason for // this is, the user may have a contact whose name happens to match a valid word in @@ -235,7 +235,7 @@ public final class Suggest { SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer); // For some reason some suggestions with MIN_VALUE are making their way here. - // TODO: Find a more robust way to detect distractors. + // TODO: Find a more robust way to detect distracters. for (int i = suggestionsContainer.size() - 1; i >= 0; --i) { if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) { suggestionsContainer.remove(i); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 32d1fe372..567aa07f1 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -49,6 +49,7 @@ public final class WordComposer { private final ArrayList<Event> mEvents; private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH); private String mAutoCorrection; + private String mAutoCorrectionDictionaryType; private boolean mIsResumed; private boolean mIsBatchMode; // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user @@ -418,8 +419,9 @@ public final class WordComposer { /** * Sets the auto-correction for this word. */ - public void setAutoCorrection(final String correction) { + public void setAutoCorrection(final String correction, String dictType) { mAutoCorrection = correction; + mAutoCorrectionDictionaryType = dictType; } /** @@ -430,6 +432,13 @@ public final class WordComposer { } /** + * @return the auto-correction dictionary type or null if none. + */ + public String getAutoCorrectionDictionaryTypeOrNull() { + return mAutoCorrectionDictionaryType; + } + + /** * @return whether we started composing this word by resuming suggestion on an existing string */ public boolean isResumed() { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index c5e60d677..0942c078f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -612,14 +612,21 @@ public final class InputLogic { final SettingsValues settingsValues, final LatinIME.UIHandler handler) { if (SuggestedWords.EMPTY != suggestedWords) { final String autoCorrection; + final String dictType; if (suggestedWords.mWillAutoCorrect) { - autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); + SuggestedWordInfo info = suggestedWords.getInfo( + SuggestedWords.INDEX_OF_AUTO_CORRECTION); + autoCorrection = info.mWord; + dictType = info.mSourceDict.mDictType; } else { // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) // because it may differ from mWordComposer.mTypedWord. autoCorrection = suggestedWords.mTypedWord; + dictType = Dictionary.TYPE_USER_TYPED; } - mWordComposer.setAutoCorrection(autoCorrection); + // TODO: Use the SuggestedWordInfo to set the auto correction when + // user typed word is available via SuggestedWordInfo. + mWordComposer.setAutoCorrection(autoCorrection, dictType); } mSuggestedWords = suggestedWords; final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect; @@ -2100,6 +2107,8 @@ public final class InputLogic { mConnection.commitCorrection(new CorrectionInfo( mConnection.getExpectedSelectionEnd() - autoCorrection.length(), typedWord, autoCorrection)); + StatsUtils.onAutoCorrection(typedWord, autoCorrection, mWordComposer.isBatchMode(), + mWordComposer.getAutoCorrectionDictionaryTypeOrNull()); } } } diff --git a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java index 5a3fc3600..29289aed2 100644 --- a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java @@ -17,7 +17,6 @@ package com.android.inputmethod.latin.settings; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.preference.Preference; @@ -32,12 +31,12 @@ import com.android.inputmethod.latin.settings.RadioButtonPreference.OnRadioButto */ public final class ThemeSettingsFragment extends SubScreenFragment implements OnRadioButtonClickedListener { - private String mSelectedThemeId; + private int mSelectedThemeId; static class KeyboardThemePreference extends RadioButtonPreference { - final String mThemeId; + final int mThemeId; - KeyboardThemePreference(final Context context, final String name, final String id) { + KeyboardThemePreference(final Context context, final String name, final int id) { super(context); setTitle(name); mThemeId = id; @@ -45,14 +44,13 @@ public final class ThemeSettingsFragment extends SubScreenFragment } static void updateKeyboardThemeSummary(final Preference pref) { - final Resources res = pref.getContext().getResources(); - final SharedPreferences prefs = pref.getSharedPreferences(); - final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); - final String keyboardThemeId = String.valueOf(keyboardTheme.mThemeId); + final Context context = pref.getContext(); + final Resources res = context.getResources(); + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(context); final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names); - final String[] keyboardThemeIds = res.getStringArray(R.array.keyboard_theme_ids); + final int[] keyboardThemeIds = res.getIntArray(R.array.keyboard_theme_ids); for (int index = 0; index < keyboardThemeNames.length; index++) { - if (keyboardThemeId.equals(keyboardThemeIds[index])) { + if (keyboardTheme.mThemeId == keyboardThemeIds[index]) { pref.setSummary(keyboardThemeNames[index]); return; } @@ -64,18 +62,18 @@ public final class ThemeSettingsFragment extends SubScreenFragment super.onCreate(icicle); addPreferencesFromResource(R.xml.prefs_screen_theme); final PreferenceScreen screen = getPreferenceScreen(); + final Context context = getActivity(); final Resources res = getResources(); final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names); - final String[] keyboardThemeIds = res.getStringArray(R.array.keyboard_theme_ids); + final int[] keyboardThemeIds = res.getIntArray(R.array.keyboard_theme_ids); for (int index = 0; index < keyboardThemeNames.length; index++) { final KeyboardThemePreference pref = new KeyboardThemePreference( - getActivity(), keyboardThemeNames[index], keyboardThemeIds[index]); + context, keyboardThemeNames[index], keyboardThemeIds[index]); screen.addPreference(pref); pref.setOnRadioButtonClickedListener(this); } - final SharedPreferences prefs = getSharedPreferences(); - final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); - mSelectedThemeId = String.valueOf(keyboardTheme.mThemeId); + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(context); + mSelectedThemeId = keyboardTheme.mThemeId; } @Override @@ -106,7 +104,7 @@ public final class ThemeSettingsFragment extends SubScreenFragment final Preference preference = screen.getPreference(index); if (preference instanceof KeyboardThemePreference) { final KeyboardThemePreference pref = (KeyboardThemePreference)preference; - final boolean selected = mSelectedThemeId.equals(pref.mThemeId); + final boolean selected = (mSelectedThemeId == pref.mThemeId); pref.setSelected(selected); } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 49b34d391..352391611 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -185,7 +185,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService try { final DictionaryFacilitator dictionaryFacilitator = mDictionaryFacilitatorCache.get(locale); - return dictionaryFacilitator.hasInitializedMainDictionary(); + return dictionaryFacilitator.hasAtLeastOneInitializedMainDictionary(); } finally { mSemaphore.release(); } diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 1db525502..f8a845304 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -64,9 +64,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr private final Object mLock = new Object(); // If the score of the top suggestion exceeds this value, the tested word (e.g., - // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distractor to + // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distracter to // words in dictionary. The greater the threshold is, the less likely the tested word would - // become a distractor, which means the tested word will be more likely to be added to + // become a distracter, which means the tested word will be more likely to be added to // the dictionary. private static final float DISTRACTER_WORD_SCORE_THRESHOLD = 0.4f; @@ -196,7 +196,7 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr } final boolean Word = dictionaryFacilitator.isValidWord(testedWord, false /* ignoreCase */); if (Word) { - // Valid word is not a distractor. + // Valid word is not a distracter. if (DEBUG) { Log.d(TAG, "isDistracter: false (valid word)"); } @@ -257,12 +257,12 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return false; } final SuggestedWordInfo firstSuggestion = suggestionResults.first(); - final boolean isDistractor = suggestionExceedsDistracterThreshold( + final boolean isDistracter = suggestionExceedsDistracterThreshold( firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD); if (DEBUG) { - Log.d(TAG, "isDistracter: " + isDistractor); + Log.d(TAG, "isDistracter: " + isDistracter); } - return isDistractor; + return isDistracter; } private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion, diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 81e2ff548..440b963d9 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -259,20 +259,21 @@ static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jintArray word) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) return NOT_A_PROBABILITY; - const jsize wordLength = env->GetArrayLength(word); - int codePoints[wordLength]; - env->GetIntArrayRegion(word, 0, wordLength, codePoints); - return dictionary->getProbability(codePoints, wordLength); + const jsize codePointCount = env->GetArrayLength(word); + int codePoints[codePointCount]; + env->GetIntArrayRegion(word, 0, codePointCount, codePoints); + return dictionary->getProbability(CodePointArrayView(codePoints, codePointCount)); } static jint latinime_BinaryDictionary_getMaxProbabilityOfExactMatches( JNIEnv *env, jclass clazz, jlong dict, jintArray word) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) return NOT_A_PROBABILITY; - const jsize wordLength = env->GetArrayLength(word); - int codePoints[wordLength]; - env->GetIntArrayRegion(word, 0, wordLength, codePoints); - return dictionary->getMaxProbabilityOfExactMatches(codePoints, wordLength); + const jsize codePointCount = env->GetArrayLength(word); + int codePoints[codePointCount]; + env->GetIntArrayRegion(word, 0, codePointCount, codePoints); + return dictionary->getMaxProbabilityOfExactMatches( + CodePointArrayView(codePoints, codePointCount)); } static jint latinime_BinaryDictionary_getNgramProbability(JNIEnv *env, jclass clazz, @@ -285,7 +286,8 @@ static jint latinime_BinaryDictionary_getNgramProbability(JNIEnv *env, jclass cl env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints); const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, prevWordCodePointArrays, isBeginningOfSentenceArray); - return dictionary->getNgramProbability(&prevWordsInfo, wordCodePoints, wordLength); + return dictionary->getNgramProbability(&prevWordsInfo, + CodePointArrayView(wordCodePoints, wordLength)); } // Method to iterate all words in the dictionary for makedict. @@ -340,7 +342,8 @@ static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz, return; } } - const WordProperty wordProperty = dictionary->getWordProperty(wordCodePoints, codePointCount); + const WordProperty wordProperty = dictionary->getWordProperty( + CodePointArrayView(wordCodePoints, codePointCount)); wordProperty.outputProperties(env, outCodePoints, outFlags, outProbabilityInfo, outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); @@ -366,7 +369,8 @@ static bool latinime_BinaryDictionary_addUnigramEntry(JNIEnv *env, jclass clazz, // Use 1 for count to indicate the word has inputted. const UnigramProperty unigramProperty(isBeginningOfSentence, isNotAWord, isBlacklisted, probability, timestamp, 0 /* level */, 1 /* count */, &shortcuts); - return dictionary->addUnigramEntry(codePoints, codePointCount, &unigramProperty); + return dictionary->addUnigramEntry(CodePointArrayView(codePoints, codePointCount), + &unigramProperty); } static bool latinime_BinaryDictionary_removeUnigramEntry(JNIEnv *env, jclass clazz, jlong dict, @@ -378,7 +382,7 @@ static bool latinime_BinaryDictionary_removeUnigramEntry(JNIEnv *env, jclass cla jsize codePointCount = env->GetArrayLength(word); int codePoints[codePointCount]; env->GetIntArrayRegion(word, 0, codePointCount, codePoints); - return dictionary->removeUnigramEntry(codePoints, codePointCount); + return dictionary->removeUnigramEntry(CodePointArrayView(codePoints, codePointCount)); } static bool latinime_BinaryDictionary_addNgramEntry(JNIEnv *env, jclass clazz, jlong dict, @@ -410,10 +414,11 @@ static bool latinime_BinaryDictionary_removeNgramEntry(JNIEnv *env, jclass clazz } const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env, prevWordCodePointArrays, isBeginningOfSentenceArray); - jsize wordLength = env->GetArrayLength(word); - int wordCodePoints[wordLength]; - env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints); - return dictionary->removeNgramEntry(&prevWordsInfo, wordCodePoints, wordLength); + jsize codePointCount = env->GetArrayLength(word); + int wordCodePoints[codePointCount]; + env->GetIntArrayRegion(word, 0, codePointCount, wordCodePoints); + return dictionary->removeNgramEntry(&prevWordsInfo, + CodePointArrayView(wordCodePoints, codePointCount)); } // Returns how many language model params are processed. @@ -484,7 +489,8 @@ static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, j const UnigramProperty unigramProperty(false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, unigramProbability, timestamp, 0 /* level */, 1 /* count */, &shortcuts); - dictionary->addUnigramEntry(word1CodePoints, word1Length, &unigramProperty); + dictionary->addUnigramEntry(CodePointArrayView(word1CodePoints, word1Length), + &unigramProperty); if (word0) { jint bigramProbability = env->GetIntField(languageModelParam, bigramProbabilityFieldId); const std::vector<int> bigramTargetCodePoints( @@ -568,8 +574,8 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j // Add unigrams. do { token = dictionary->getNextWordAndNextToken(token, wordCodePoints, &wordCodePointCount); - const WordProperty wordProperty = dictionary->getWordProperty(wordCodePoints, - wordCodePointCount); + const WordProperty wordProperty = dictionary->getWordProperty( + CodePointArrayView(wordCodePoints, wordCodePointCount)); if (wordCodePoints[0] == CODE_POINT_BEGINNING_OF_SENTENCE) { // Skip beginning-of-sentence unigram. continue; @@ -593,8 +599,8 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j // Add bigrams. do { token = dictionary->getNextWordAndNextToken(token, wordCodePoints, &wordCodePointCount); - const WordProperty wordProperty = dictionary->getWordProperty(wordCodePoints, - wordCodePointCount); + const WordProperty wordProperty = dictionary->getWordProperty( + CodePointArrayView(wordCodePoints, wordCodePointCount)); if (dictionaryStructureWithBufferPolicy->needsToRunGC(true /* mindsBlockByGC */)) { dictionaryStructureWithBufferPolicy = runGCAndGetNewStructurePolicy( std::move(dictionaryStructureWithBufferPolicy), dictFilePathChars); diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h index cecfc7aa9..1b796b5d4 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h @@ -32,7 +32,7 @@ class DicNodeProperties { public: AK_FORCE_INLINE DicNodeProperties() : mChildrenPtNodeArrayPos(NOT_A_DICT_POS), mDicNodeCodePoint(NOT_A_CODE_POINT), - mWordId(NOT_A_WORD_ID), mDepth(0), mLeavingDepth(0) {} + mWordId(NOT_A_WORD_ID), mDepth(0), mLeavingDepth(0), mPrevWordCount(0) {} ~DicNodeProperties() {} @@ -45,6 +45,7 @@ class DicNodeProperties { mDepth = depth; mLeavingDepth = leavingDepth; prevWordIds.copyToArray(&mPrevWordIds, 0 /* offset */); + mPrevWordCount = prevWordIds.size(); } // Init for root with prevWordsPtNodePos which is used for n-gram @@ -55,6 +56,7 @@ class DicNodeProperties { mDepth = 0; mLeavingDepth = 0; prevWordIds.copyToArray(&mPrevWordIds, 0 /* offset */); + mPrevWordCount = prevWordIds.size(); } void initByCopy(const DicNodeProperties *const dicNodeProp) { @@ -63,8 +65,9 @@ class DicNodeProperties { mWordId = dicNodeProp->mWordId; mDepth = dicNodeProp->mDepth; mLeavingDepth = dicNodeProp->mLeavingDepth; - WordIdArrayView::fromArray(dicNodeProp->mPrevWordIds) - .copyToArray(&mPrevWordIds, 0 /* offset */); + const WordIdArrayView prevWordIdArrayView = dicNodeProp->getPrevWordIds(); + prevWordIdArrayView.copyToArray(&mPrevWordIds, 0 /* offset */); + mPrevWordCount = prevWordIdArrayView.size(); } // Init as passing child @@ -74,8 +77,9 @@ class DicNodeProperties { mWordId = dicNodeProp->mWordId; mDepth = dicNodeProp->mDepth + 1; // Increment the depth of a passing child mLeavingDepth = dicNodeProp->mLeavingDepth; - WordIdArrayView::fromArray(dicNodeProp->mPrevWordIds) - .copyToArray(&mPrevWordIds, 0 /* offset */); + const WordIdArrayView prevWordIdArrayView = dicNodeProp->getPrevWordIds(); + prevWordIdArrayView.copyToArray(&mPrevWordIds, 0 /* offset */); + mPrevWordCount = prevWordIdArrayView.size(); } int getChildrenPtNodeArrayPos() const { @@ -104,7 +108,7 @@ class DicNodeProperties { } const WordIdArrayView getPrevWordIds() const { - return WordIdArrayView::fromArray(mPrevWordIds); + return WordIdArrayView::fromArray(mPrevWordIds).limit(mPrevWordCount); } int getWordId() const { @@ -121,6 +125,7 @@ class DicNodeProperties { uint16_t mDepth; uint16_t mLeavingDepth; WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> mPrevWordIds; + size_t mPrevWordCount; }; } // namespace latinime #endif // LATINIME_DIC_NODE_PROPERTIES_H diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index ec261cfbf..f9f36ce44 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -93,42 +93,42 @@ void Dictionary::NgramListenerForPrediction::onVisitEntry(const int ngramProbabi void Dictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIds; - prevWordsInfo->getPrevWordIds(mDictionaryStructureWithBufferPolicy.get(), prevWordIds.data(), + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds( + mDictionaryStructureWithBufferPolicy.get(), &prevWordIdArray, true /* tryLowerCaseSearch */); - const WordIdArrayView prevWordIdArrayView = WordIdArrayView::fromArray(prevWordIds); - NgramListenerForPrediction listener(prevWordsInfo, prevWordIdArrayView, outSuggestionResults, + NgramListenerForPrediction listener(prevWordsInfo, prevWordIds, outSuggestionResults, mDictionaryStructureWithBufferPolicy.get()); - mDictionaryStructureWithBufferPolicy->iterateNgramEntries(prevWordIdArrayView, &listener); + mDictionaryStructureWithBufferPolicy->iterateNgramEntries(prevWordIds, &listener); } -int Dictionary::getProbability(const int *word, int length) const { - return getNgramProbability(nullptr /* prevWordsInfo */, word, length); +int Dictionary::getProbability(const CodePointArrayView codePoints) const { + return getNgramProbability(nullptr /* prevWordsInfo */, codePoints); } -int Dictionary::getMaxProbabilityOfExactMatches(const int *word, int length) const { +int Dictionary::getMaxProbabilityOfExactMatches(const CodePointArrayView codePoints) const { TimeKeeper::setCurrentTime(); return DictionaryUtils::getMaxProbabilityOfExactMatches( - mDictionaryStructureWithBufferPolicy.get(), word, length); + mDictionaryStructureWithBufferPolicy.get(), codePoints); } -int Dictionary::getNgramProbability(const PrevWordsInfo *const prevWordsInfo, const int *word, - int length) const { +int Dictionary::getNgramProbability(const PrevWordsInfo *const prevWordsInfo, + const CodePointArrayView codePoints) const { TimeKeeper::setCurrentTime(); - int wordId = mDictionaryStructureWithBufferPolicy->getWordId( - CodePointArrayView(word, length), false /* forceLowerCaseSearch */); + const int wordId = mDictionaryStructureWithBufferPolicy->getWordId(codePoints, + false /* forceLowerCaseSearch */); if (wordId == NOT_A_WORD_ID) return NOT_A_PROBABILITY; if (!prevWordsInfo) { return getDictionaryStructurePolicy()->getProbabilityOfWord(WordIdArrayView(), wordId); } - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIds; - prevWordsInfo->getPrevWordIds(mDictionaryStructureWithBufferPolicy.get(), prevWordIds.data(), + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds + (mDictionaryStructureWithBufferPolicy.get(), &prevWordIdArray, true /* tryLowerCaseSearch */); - return getDictionaryStructurePolicy()->getProbabilityOfWord( - IntArrayView::fromArray(prevWordIds), wordId); + return getDictionaryStructurePolicy()->getProbabilityOfWord(prevWordIds, wordId); } -bool Dictionary::addUnigramEntry(const int *const word, const int length, +bool Dictionary::addUnigramEntry(const CodePointArrayView codePoints, const UnigramProperty *const unigramProperty) { if (unigramProperty->representsBeginningOfSentence() && !mDictionaryStructureWithBufferPolicy->getHeaderStructurePolicy() @@ -137,14 +137,12 @@ bool Dictionary::addUnigramEntry(const int *const word, const int length, return false; } TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->addUnigramEntry(CodePointArrayView(word, length), - unigramProperty); + return mDictionaryStructureWithBufferPolicy->addUnigramEntry(codePoints, unigramProperty); } -bool Dictionary::removeUnigramEntry(const int *const codePoints, const int codePointCount) { +bool Dictionary::removeUnigramEntry(const CodePointArrayView codePoints) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->removeUnigramEntry( - CodePointArrayView(codePoints, codePointCount)); + return mDictionaryStructureWithBufferPolicy->removeUnigramEntry(codePoints); } bool Dictionary::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, @@ -154,10 +152,9 @@ bool Dictionary::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, } bool Dictionary::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, - const int *const word, const int length) { + const CodePointArrayView codePoints) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, - CodePointArrayView(word, length)); + return mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, codePoints); } bool Dictionary::flush(const char *const filePath) { @@ -182,11 +179,9 @@ void Dictionary::getProperty(const char *const query, const int queryLength, cha maxResultLength); } -const WordProperty Dictionary::getWordProperty(const int *const codePoints, - const int codePointCount) { +const WordProperty Dictionary::getWordProperty(const CodePointArrayView codePoints) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy->getWordProperty( - CodePointArrayView(codePoints, codePointCount)); + return mDictionaryStructureWithBufferPolicy->getWordProperty(codePoints); } int Dictionary::getNextWordAndNextToken(const int token, int *const outCodePoints, diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index 0b54f30e9..f6482ab78 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -72,23 +72,23 @@ class Dictionary { void getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const; - int getProbability(const int *word, int length) const; + int getProbability(const CodePointArrayView codePoints) const; - int getMaxProbabilityOfExactMatches(const int *word, int length) const; + int getMaxProbabilityOfExactMatches(const CodePointArrayView codePoints) const; int getNgramProbability(const PrevWordsInfo *const prevWordsInfo, - const int *word, int length) const; + const CodePointArrayView codePoints) const; - bool addUnigramEntry(const int *const codePoints, const int codePointCount, + bool addUnigramEntry(const CodePointArrayView codePoints, const UnigramProperty *const unigramProperty); - bool removeUnigramEntry(const int *const codePoints, const int codePointCount); + bool removeUnigramEntry(const CodePointArrayView codePoints); bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, const BigramProperty *const bigramProperty); - bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word, - const int length); + bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const CodePointArrayView codePoints); bool flush(const char *const filePath); @@ -99,7 +99,7 @@ class Dictionary { void getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength); - const WordProperty getWordProperty(const int *const codePoints, const int codePointCount); + const WordProperty getWordProperty(const CodePointArrayView codePoints); // Method to iterate all words in the dictionary. // The returned token has to be used to get the next word. If token is 0, this method newly diff --git a/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp b/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp index 617bf5a90..b85f3622a 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp @@ -29,28 +29,27 @@ namespace latinime { /* static */ int DictionaryUtils::getMaxProbabilityOfExactMatches( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const int *const codePoints, const int codePointCount) { + const CodePointArrayView codePoints) { std::vector<DicNode> current; std::vector<DicNode> next; // No prev words information. PrevWordsInfo emptyPrevWordsInfo; - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIds; - emptyPrevWordsInfo.getPrevWordIds(dictionaryStructurePolicy, prevWordIds.data(), - false /* tryLowerCaseSearch */); + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = emptyPrevWordsInfo.getPrevWordIds( + dictionaryStructurePolicy, &prevWordIdArray, false /* tryLowerCaseSearch */); current.emplace_back(); - DicNodeUtils::initAsRoot(dictionaryStructurePolicy, - IntArrayView::fromArray(prevWordIds), ¤t.front()); - for (int i = 0; i < codePointCount; ++i) { + DicNodeUtils::initAsRoot(dictionaryStructurePolicy, prevWordIds, ¤t.front()); + for (const int codePoint : codePoints) { // The base-lower input is used to ignore case errors and accent errors. - const int codePoint = CharUtils::toBaseLowerCase(codePoints[i]); + const int baseLowerCodePoint = CharUtils::toBaseLowerCase(codePoint); for (const DicNode &dicNode : current) { - if (dicNode.isInDigraph() && dicNode.getNodeCodePoint() == codePoint) { + if (dicNode.isInDigraph() && dicNode.getNodeCodePoint() == baseLowerCodePoint) { next.emplace_back(dicNode); next.back().advanceDigraphIndex(); continue; } - processChildDicNodes(dictionaryStructurePolicy, codePoint, &dicNode, &next); + processChildDicNodes(dictionaryStructurePolicy, baseLowerCodePoint, &dicNode, &next); } current.clear(); current.swap(next); diff --git a/native/jni/src/suggest/core/dictionary/dictionary_utils.h b/native/jni/src/suggest/core/dictionary/dictionary_utils.h index 358ebf674..4dd21c9be 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary_utils.h +++ b/native/jni/src/suggest/core/dictionary/dictionary_utils.h @@ -20,6 +20,7 @@ #include <vector> #include "defines.h" +#include "utils/int_array_view.h" namespace latinime { @@ -30,7 +31,7 @@ class DictionaryUtils { public: static int getMaxProbabilityOfExactMatches( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const int *const codePoints, const int codePointCount); + const CodePointArrayView codePoints); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryUtils); diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 4f58c0c54..4d7505a55 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -35,8 +35,8 @@ void DicTraverseSession::init(const Dictionary *const dictionary, mMultiWordCostMultiplier = getDictionaryStructurePolicy()->getHeaderStructurePolicy() ->getMultiWordCostMultiplier(); mSuggestOptions = suggestOptions; - prevWordsInfo->getPrevWordIds(getDictionaryStructurePolicy(), mPrevWordsIds.data(), - true /* tryLowerCaseSearch */); + mPrevWordIdCount = prevWordsInfo->getPrevWordIds(getDictionaryStructurePolicy(), + &mPrevWordIdArray, true /* tryLowerCaseSearch */).size(); } void DicTraverseSession::setupForGetSuggestions(const ProximityInfo *pInfo, diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index af199071e..9f841aa3c 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -51,12 +51,11 @@ class DicTraverseSession { } AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache) - : mProximityInfo(nullptr), mDictionary(nullptr), mSuggestOptions(nullptr), - mDicNodesCache(usesLargeCache), mMultiBigramMap(), mInputSize(0), mMaxPointerCount(1), - mMultiWordCostMultiplier(1.0f) { + : mPrevWordIdCount(0), mProximityInfo(nullptr), mDictionary(nullptr), + mSuggestOptions(nullptr), mDicNodesCache(usesLargeCache), mMultiBigramMap(), + mInputSize(0), mMaxPointerCount(1), mMultiWordCostMultiplier(1.0f) { // NOTE: mProximityInfoStates is an array of instances. // No need to initialize it explicitly here. - mPrevWordsIds.fill(NOT_A_DICT_POS); } // Non virtual inline destructor -- never inherit this class @@ -78,7 +77,9 @@ class DicTraverseSession { //-------------------- const ProximityInfo *getProximityInfo() const { return mProximityInfo; } const SuggestOptions *getSuggestOptions() const { return mSuggestOptions; } - const WordIdArrayView getPrevWordIds() const { return IntArrayView::fromArray(mPrevWordsIds); } + const WordIdArrayView getPrevWordIds() const { + return WordIdArrayView::fromArray(mPrevWordIdArray).limit(mPrevWordIdCount); + } DicNodesCache *getDicTraverseCache() { return &mDicNodesCache; } MultiBigramMap *getMultiBigramMap() { return &mMultiBigramMap; } const ProximityInfoState *getProximityInfoState(int id) const { @@ -165,7 +166,8 @@ class DicTraverseSession { const int *const inputYs, const int *const times, const int *const pointerIds, const int inputSize, const float maxSpatialDistance, const int maxPointerCount); - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> mPrevWordsIds; + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> mPrevWordIdArray; + size_t mPrevWordIdCount; const ProximityInfo *mProximityInfo; const Dictionary *mDictionary; const SuggestOptions *mSuggestOptions; diff --git a/native/jni/src/suggest/core/session/prev_words_info.h b/native/jni/src/suggest/core/session/prev_words_info.h index fc9a35968..02e82a8e0 100644 --- a/native/jni/src/suggest/core/session/prev_words_info.h +++ b/native/jni/src/suggest/core/session/prev_words_info.h @@ -17,6 +17,8 @@ #ifndef LATINIME_PREV_WORDS_INFO_H #define LATINIME_PREV_WORDS_INFO_H +#include <array> + #include "defines.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "utils/char_utils.h" @@ -27,12 +29,13 @@ namespace latinime { class PrevWordsInfo { public: // No prev word information. - PrevWordsInfo() { + PrevWordsInfo() : mPrevWordCount(0) { clear(); } - PrevWordsInfo(PrevWordsInfo &&prevWordsInfo) { - for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { + PrevWordsInfo(PrevWordsInfo &&prevWordsInfo) + : mPrevWordCount(prevWordsInfo.mPrevWordCount) { + for (size_t i = 0; i < mPrevWordCount; ++i) { mPrevWordCodePointCount[i] = prevWordsInfo.mPrevWordCodePointCount[i]; memmove(mPrevWordCodePoints[i], prevWordsInfo.mPrevWordCodePoints[i], sizeof(mPrevWordCodePoints[i][0]) * mPrevWordCodePointCount[i]); @@ -43,9 +46,10 @@ class PrevWordsInfo { // Construct from previous words. PrevWordsInfo(const int prevWordCodePoints[][MAX_WORD_LENGTH], const int *const prevWordCodePointCount, const bool *const isBeginningOfSentence, - const size_t prevWordCount) { + const size_t prevWordCount) + : mPrevWordCount(std::min(NELEMS(mPrevWordCodePoints), prevWordCount)) { clear(); - for (size_t i = 0; i < std::min(NELEMS(mPrevWordCodePoints), prevWordCount); ++i) { + for (size_t i = 0; i < mPrevWordCount; ++i) { if (prevWordCodePointCount[i] < 0 || prevWordCodePointCount[i] > MAX_WORD_LENGTH) { continue; } @@ -58,7 +62,7 @@ class PrevWordsInfo { // Construct from a previous word. PrevWordsInfo(const int *const prevWordCodePoints, const int prevWordCodePointCount, - const bool isBeginningOfSentence) { + const bool isBeginningOfSentence) : mPrevWordCount(1) { clear(); if (prevWordCodePointCount > MAX_WORD_LENGTH || !prevWordCodePoints) { return; @@ -79,26 +83,29 @@ class PrevWordsInfo { return false; } - void getPrevWordIds(const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, - int *const outPrevWordIds, const bool tryLowerCaseSearch) const { - for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { - outPrevWordIds[i] = getWordId(dictStructurePolicy, + template<size_t N> + const WordIdArrayView getPrevWordIds( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, + std::array<int, N> *const prevWordIdBuffer, const bool tryLowerCaseSearch) const { + for (size_t i = 0; i < std::min(mPrevWordCount, N); ++i) { + prevWordIdBuffer->at(i) = getWordId(dictStructurePolicy, mPrevWordCodePoints[i], mPrevWordCodePointCount[i], mIsBeginningOfSentence[i], tryLowerCaseSearch); } + return WordIdArrayView::fromArray(*prevWordIdBuffer).limit(mPrevWordCount); } // n is 1-indexed. - const CodePointArrayView getNthPrevWordCodePoints(const int n) const { - if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + const CodePointArrayView getNthPrevWordCodePoints(const size_t n) const { + if (n <= 0 || n > mPrevWordCount) { return CodePointArrayView(); } return CodePointArrayView(mPrevWordCodePoints[n - 1], mPrevWordCodePointCount[n - 1]); } // n is 1-indexed. - bool isNthPrevWordBeginningOfSentence(const int n) const { - if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + bool isNthPrevWordBeginningOfSentence(const size_t n) const { + if (n <= 0 || n > mPrevWordCount) { return false; } return mIsBeginningOfSentence[n - 1]; @@ -142,6 +149,7 @@ class PrevWordsInfo { } } + const size_t mPrevWordCount; int mPrevWordCodePoints[MAX_PREV_WORD_COUNT_FOR_N_GRAM][MAX_WORD_LENGTH]; int mPrevWordCodePointCount[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; bool mIsBeginningOfSentence[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp index dfc3d2d9b..ee1403739 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp @@ -268,8 +268,8 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const CodePointArrayView wordCodePo return false; } const CodePointArrayView codePointArrayView(codePointsToAdd, codePointCountToAdd); - if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointArrayView.data(), - codePointArrayView.size(), unigramProperty, &addedNewUnigram)) { + if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointArrayView, unigramProperty, + &addedNewUnigram)) { if (addedNewUnigram && !unigramProperty->representsBeginningOfSentence()) { mUnigramCount++; } @@ -283,8 +283,8 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const CodePointArrayView wordCodePo } for (const auto &shortcut : unigramProperty->getShortcuts()) { if (!mUpdatingHelper.addShortcutTarget(wordPos, - shortcut.getTargetCodePoints()->data(), - shortcut.getTargetCodePoints()->size(), shortcut.getProbability())) { + CodePointArrayView(*shortcut.getTargetCodePoints()), + shortcut.getProbability())) { AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %zd, " "probability: %d", wordPos, shortcut.getTargetCodePoints()->size(), shortcut.getProbability()); @@ -332,8 +332,12 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI "length: %zd", bigramProperty->getTargetCodePoints()->size()); return false; } - int prevWordIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; - prevWordsInfo->getPrevWordIds(this, prevWordIds, false /* tryLowerCaseSearch */); + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + false /* tryLowerCaseSearch */); + if (prevWordIds.empty()) { + return false; + } if (prevWordIds[0] == NOT_A_WORD_ID) { if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) { const std::vector<UnigramProperty::ShortcutProperty> shortcuts; @@ -347,7 +351,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI return false; } // Refresh word ids. - prevWordsInfo->getPrevWordIds(this, prevWordIds, false /* tryLowerCaseSearch */); + prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); } else { return false; } @@ -390,9 +394,10 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor AKLOGE("word is too long to remove n-gram entry form the dictionary. length: %zd", wordCodePoints.size()); } - int prevWordIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; - prevWordsInfo->getPrevWordIds(this, prevWordIds, false /* tryLowerCaseSerch */); - if (prevWordIds[0] == NOT_A_WORD_ID) { + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + false /* tryLowerCaseSerch */); + if (prevWordIds.firstOrDefault(NOT_A_WORD_ID) == NOT_A_WORD_ID) { return false; } const int wordPos = getTerminalPtNodePosFromWordId(getWordId(wordCodePoints, diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp index f7fd5c071..1b2f857ab 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp @@ -39,32 +39,31 @@ const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F; /* static */ bool BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( - const uint8_t *const bigramsBuf, const int bufSize, BigramFlags *const outBigramFlags, + const ReadOnlyByteArrayView buffer, BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, int *const bigramEntryPos) { - if (bufSize <= *bigramEntryPos) { - AKLOGE("Read invalid pos in getBigramEntryPropertiesAndAdvancePosition(). bufSize: %d, " - "bigramEntryPos: %d.", bufSize, *bigramEntryPos); + if (static_cast<int>(buffer.size()) <= *bigramEntryPos) { + AKLOGE("Read invalid pos in getBigramEntryPropertiesAndAdvancePosition(). bufSize: %zd, " + "bigramEntryPos: %d.", buffer.size(), *bigramEntryPos); return false; } - const BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf, + const BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition(buffer.data(), bigramEntryPos); if (outBigramFlags) { *outBigramFlags = bigramFlags; } - const int targetPos = getBigramAddressAndAdvancePosition(bigramsBuf, bigramFlags, - bigramEntryPos); + const int targetPos = getBigramAddressAndAdvancePosition(buffer, bigramFlags, bigramEntryPos); if (outTargetPtNodePos) { *outTargetPtNodePos = targetPos; } return true; } -/* static */ bool BigramListReadWriteUtils::skipExistingBigrams(const uint8_t *const bigramsBuf, - const int bufSize, int *const bigramListPos) { +/* static */ bool BigramListReadWriteUtils::skipExistingBigrams(const ReadOnlyByteArrayView buffer, + int *const bigramListPos) { BigramFlags flags; do { - if (!getBigramEntryPropertiesAndAdvancePosition(bigramsBuf, bufSize, &flags, - 0 /* outTargetPtNodePos */, bigramListPos)) { + if (!getBigramEntryPropertiesAndAdvancePosition(buffer, &flags, 0 /* outTargetPtNodePos */, + bigramListPos)) { return false; } } while(hasNext(flags)); @@ -72,18 +71,18 @@ const BigramListReadWriteUtils::BigramFlags } /* static */ int BigramListReadWriteUtils::getBigramAddressAndAdvancePosition( - const uint8_t *const bigramsBuf, const BigramFlags flags, int *const pos) { + const ReadOnlyByteArrayView buffer, const BigramFlags flags, int *const pos) { int offset = 0; const int origin = *pos; switch (MASK_ATTRIBUTE_ADDRESS_TYPE & flags) { case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: - offset = ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf, pos); + offset = ByteArrayUtils::readUint8AndAdvancePosition(buffer.data(), pos); break; case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: - offset = ByteArrayUtils::readUint16AndAdvancePosition(bigramsBuf, pos); + offset = ByteArrayUtils::readUint16AndAdvancePosition(buffer.data(), pos); break; case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES: - offset = ByteArrayUtils::readUint24AndAdvancePosition(bigramsBuf, pos); + offset = ByteArrayUtils::readUint24AndAdvancePosition(buffer.data(), pos); break; } if (isOffsetNegative(flags)) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h index 10f93fb7a..a0f7d5e83 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h @@ -21,6 +21,7 @@ #include <cstdlib> #include "defines.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -30,8 +31,8 @@ class BigramListReadWriteUtils { public: typedef uint8_t BigramFlags; - static bool getBigramEntryPropertiesAndAdvancePosition(const uint8_t *const bigramsBuf, - const int bufSize, BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, + static bool getBigramEntryPropertiesAndAdvancePosition(const ReadOnlyByteArrayView buffer, + BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, int *const bigramEntryPos); static AK_FORCE_INLINE int getProbabilityFromFlags(const BigramFlags flags) { @@ -43,8 +44,7 @@ public: } // Bigrams reading methods - static bool skipExistingBigrams(const uint8_t *const bigramsBuf, const int bufSize, - int *const bigramListPos); + static bool skipExistingBigrams(const ReadOnlyByteArrayView buffer, int *const bigramListPos); private: DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListReadWriteUtils); @@ -61,7 +61,7 @@ private: return (flags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) != 0; } - static int getBigramAddressAndAdvancePosition(const uint8_t *const bigramsBuf, + static int getBigramAddressAndAdvancePosition(const ReadOnlyByteArrayView buffer, const BigramFlags flags, int *const pos); }; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp index 086d98b4a..40782a44f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp @@ -218,9 +218,9 @@ int DynamicPtReadingHelper::getCodePointsAndProbabilityAndReturnCodePointCount( } int DynamicPtReadingHelper::getTerminalPtNodePositionOfWord(const int *const inWord, - const int length, const bool forceLowerCaseSearch) { + const size_t length, const bool forceLowerCaseSearch) { int searchCodePoints[length]; - for (int i = 0; i < length; ++i) { + for (size_t i = 0; i < length; ++i) { searchCodePoints[i] = forceLowerCaseSearch ? CharUtils::toLowerCase(inWord[i]) : inWord[i]; } while (!isEnd()) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h index b7262581a..9a7abc97f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h @@ -138,12 +138,12 @@ class DynamicPtReadingHelper { } // Return code point count exclude the last read node's code points. - AK_FORCE_INLINE int getPrevTotalCodePointCount() const { + AK_FORCE_INLINE size_t getPrevTotalCodePointCount() const { return mReadingState.mTotalCodePointCountSinceInitialization; } // Return code point count include the last read node's code points. - AK_FORCE_INLINE int getTotalCodePointCount(const PtNodeParams &ptNodeParams) const { + AK_FORCE_INLINE size_t getTotalCodePointCount(const PtNodeParams &ptNodeParams) const { return mReadingState.mTotalCodePointCountSinceInitialization + ptNodeParams.getCodePointCount(); } @@ -214,7 +214,7 @@ class DynamicPtReadingHelper { int getCodePointsAndProbabilityAndReturnCodePointCount(const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability); - int getTerminalPtNodePositionOfWord(const int *const inWord, const int length, + int getTerminalPtNodePositionOfWord(const int *const inWord, const size_t length, const bool forceLowerCaseSearch); private: @@ -234,7 +234,7 @@ class DynamicPtReadingHelper { int mPos; // Remaining node count in the current array. int mRemainingPtNodeCountInThisArray; - int mTotalCodePointCountSinceInitialization; + size_t mTotalCodePointCountSinceInitialization; // Counter of PtNodes used to avoid infinite loops caused by broken or malicious links. int mTotalPtNodeIndexInThisArrayChain; // Counter of PtNode arrays used to avoid infinite loops caused by cyclic links of empty diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp index 3c62e2e56..3b58d7d6d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp @@ -28,17 +28,16 @@ namespace latinime { const int DynamicPtUpdatingHelper::CHILDREN_POSITION_FIELD_SIZE = 3; -bool DynamicPtUpdatingHelper::addUnigramWord( - DynamicPtReadingHelper *const readingHelper, - const int *const wordCodePoints, const int codePointCount, - const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram) { +bool DynamicPtUpdatingHelper::addUnigramWord(DynamicPtReadingHelper *const readingHelper, + const CodePointArrayView wordCodePoints, const UnigramProperty *const unigramProperty, + bool *const outAddedNewUnigram) { int parentPos = NOT_A_DICT_POS; while (!readingHelper->isEnd()) { const PtNodeParams ptNodeParams(readingHelper->getPtNodeParams()); if (!ptNodeParams.isValid()) { break; } - const int matchedCodePointCount = readingHelper->getPrevTotalCodePointCount(); + const size_t matchedCodePointCount = readingHelper->getPrevTotalCodePointCount(); if (!readingHelper->isMatchedCodePoint(ptNodeParams, 0 /* index */, wordCodePoints[matchedCodePointCount])) { // The first code point is different from target code point. Skip this node and read @@ -47,26 +46,25 @@ bool DynamicPtUpdatingHelper::addUnigramWord( continue; } // Check following merged node code points. - const int nodeCodePointCount = ptNodeParams.getCodePointCount(); - for (int j = 1; j < nodeCodePointCount; ++j) { - const int nextIndex = matchedCodePointCount + j; - if (nextIndex >= codePointCount || !readingHelper->isMatchedCodePoint(ptNodeParams, j, - wordCodePoints[matchedCodePointCount + j])) { + const size_t nodeCodePointCount = ptNodeParams.getCodePointArrayView().size(); + for (size_t j = 1; j < nodeCodePointCount; ++j) { + const size_t nextIndex = matchedCodePointCount + j; + if (nextIndex >= wordCodePoints.size() + || !readingHelper->isMatchedCodePoint(ptNodeParams, j, + wordCodePoints[matchedCodePointCount + j])) { *outAddedNewUnigram = true; return reallocatePtNodeAndAddNewPtNodes(&ptNodeParams, j, unigramProperty, - wordCodePoints + matchedCodePointCount, - codePointCount - matchedCodePointCount); + wordCodePoints.skip(matchedCodePointCount)); } } // All characters are matched. - if (codePointCount == readingHelper->getTotalCodePointCount(ptNodeParams)) { + if (wordCodePoints.size() == readingHelper->getTotalCodePointCount(ptNodeParams)) { return setPtNodeProbability(&ptNodeParams, unigramProperty, outAddedNewUnigram); } if (!ptNodeParams.hasChildren()) { *outAddedNewUnigram = true; return createChildrenPtNodeArrayAndAChildPtNode(&ptNodeParams, unigramProperty, - wordCodePoints + readingHelper->getTotalCodePointCount(ptNodeParams), - codePointCount - readingHelper->getTotalCodePointCount(ptNodeParams)); + wordCodePoints.skip(readingHelper->getTotalCodePointCount(ptNodeParams))); } // Advance to the children nodes. parentPos = ptNodeParams.getHeadPos(); @@ -79,9 +77,8 @@ bool DynamicPtUpdatingHelper::addUnigramWord( int pos = readingHelper->getPosOfLastForwardLinkField(); *outAddedNewUnigram = true; return createAndInsertNodeIntoPtNodeArray(parentPos, - wordCodePoints + readingHelper->getPrevTotalCodePointCount(), - codePointCount - readingHelper->getPrevTotalCodePointCount(), - unigramProperty, &pos); + wordCodePoints.skip(readingHelper->getPrevTotalCodePointCount()), unigramProperty, + &pos); } bool DynamicPtUpdatingHelper::addNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, @@ -120,23 +117,21 @@ bool DynamicPtUpdatingHelper::removeNgramEntry(const PtNodePosArrayView prevWord } bool DynamicPtUpdatingHelper::addShortcutTarget(const int wordPos, - const int *const targetCodePoints, const int targetCodePointCount, - const int shortcutProbability) { + const CodePointArrayView targetCodePoints, const int shortcutProbability) { const PtNodeParams ptNodeParams(mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(wordPos)); - return mPtNodeWriter->addShortcutTarget(&ptNodeParams, targetCodePoints, targetCodePointCount, - shortcutProbability); + return mPtNodeWriter->addShortcutTarget(&ptNodeParams, targetCodePoints.data(), + targetCodePoints.size(), shortcutProbability); } bool DynamicPtUpdatingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos, - const int *const nodeCodePoints, const int nodeCodePointCount, - const UnigramProperty *const unigramProperty, int *const forwardLinkFieldPos) { + const CodePointArrayView ptNodeCodePoints, const UnigramProperty *const unigramProperty, + int *const forwardLinkFieldPos) { const int newPtNodeArrayPos = mBuffer->getTailPosition(); if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, newPtNodeArrayPos, forwardLinkFieldPos)) { return false; } - return createNewPtNodeArrayWithAChildPtNode(parentPos, nodeCodePoints, nodeCodePointCount, - unigramProperty); + return createNewPtNodeArrayWithAChildPtNode(parentPos, ptNodeCodePoints, unigramProperty); } bool DynamicPtUpdatingHelper::setPtNodeProbability(const PtNodeParams *const originalPtNodeParams, @@ -153,8 +148,7 @@ bool DynamicPtUpdatingHelper::setPtNodeProbability(const PtNodeParams *const ori const PtNodeParams ptNodeParamsToWrite(getUpdatedPtNodeParams(originalPtNodeParams, unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), true /* isTerminal */, originalPtNodeParams->getParentPos(), - originalPtNodeParams->getCodePointCount(), originalPtNodeParams->getCodePoints(), - unigramProperty->getProbability())); + originalPtNodeParams->getCodePointArrayView(), unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, unigramProperty, &writingPos)) { return false; @@ -168,17 +162,17 @@ bool DynamicPtUpdatingHelper::setPtNodeProbability(const PtNodeParams *const ori bool DynamicPtUpdatingHelper::createChildrenPtNodeArrayAndAChildPtNode( const PtNodeParams *const parentPtNodeParams, const UnigramProperty *const unigramProperty, - const int *const codePoints, const int codePointCount) { + const CodePointArrayView codePoints) { const int newPtNodeArrayPos = mBuffer->getTailPosition(); if (!mPtNodeWriter->updateChildrenPosition(parentPtNodeParams, newPtNodeArrayPos)) { return false; } return createNewPtNodeArrayWithAChildPtNode(parentPtNodeParams->getHeadPos(), codePoints, - codePointCount, unigramProperty); + unigramProperty); } bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( - const int parentPtNodePos, const int *const nodeCodePoints, const int nodeCodePointCount, + const int parentPtNodePos, const CodePointArrayView ptNodeCodePoints, const UnigramProperty *const unigramProperty) { int writingPos = mBuffer->getTailPosition(); if (!DynamicPtWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, @@ -187,8 +181,7 @@ bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( } const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), true /* isTerminal */, - parentPtNodePos, nodeCodePointCount, nodeCodePoints, - unigramProperty->getProbability())); + parentPtNodePos, ptNodeCodePoints, unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, unigramProperty, &writingPos)) { return false; @@ -202,9 +195,9 @@ bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( // Returns whether the dictionary updating was succeeded or not. bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( - const PtNodeParams *const reallocatingPtNodeParams, const int overlappingCodePointCount, - const UnigramProperty *const unigramProperty, const int *const newNodeCodePoints, - const int newNodeCodePointCount) { + const PtNodeParams *const reallocatingPtNodeParams, const size_t overlappingCodePointCount, + const UnigramProperty *const unigramProperty, + const CodePointArrayView newPtNodeCodePoints) { // When addsExtraChild is true, split the reallocating PtNode and add new child. // Reallocating PtNode: abcde, newNode: abcxy. // abc (1st, not terminal) __ de (2nd) @@ -212,16 +205,18 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( // Otherwise, this method makes 1st part terminal and write information in unigramProperty. // Reallocating PtNode: abcde, newNode: abc. // abc (1st, terminal) __ de (2nd) - const bool addsExtraChild = newNodeCodePointCount > overlappingCodePointCount; + const bool addsExtraChild = newPtNodeCodePoints.size() > overlappingCodePointCount; const int firstPartOfReallocatedPtNodePos = mBuffer->getTailPosition(); int writingPos = firstPartOfReallocatedPtNodePos; // Write the 1st part of the reallocating node. The children position will be updated later // with actual children position. + const CodePointArrayView firstPtNodeCodePoints = + reallocatingPtNodeParams->getCodePointArrayView().limit(overlappingCodePointCount); if (addsExtraChild) { const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( false /* isNotAWord */, false /* isBlacklisted */, false /* isTerminal */, - reallocatingPtNodeParams->getParentPos(), overlappingCodePointCount, - reallocatingPtNodeParams->getCodePoints(), NOT_A_PROBABILITY)); + reallocatingPtNodeParams->getParentPos(), firstPtNodeCodePoints, + NOT_A_PROBABILITY)); if (!mPtNodeWriter->writePtNodeAndAdvancePosition(&ptNodeParamsToWrite, &writingPos)) { return false; } @@ -229,8 +224,7 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), true /* isTerminal */, reallocatingPtNodeParams->getParentPos(), - overlappingCodePointCount, reallocatingPtNodeParams->getCodePoints(), - unigramProperty->getProbability())); + firstPtNodeCodePoints, unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, unigramProperty, &writingPos)) { return false; @@ -248,8 +242,7 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( const PtNodeParams childPartPtNodeParams(getUpdatedPtNodeParams(reallocatingPtNodeParams, reallocatingPtNodeParams->isNotAWord(), reallocatingPtNodeParams->isBlacklisted(), reallocatingPtNodeParams->isTerminal(), firstPartOfReallocatedPtNodePos, - reallocatingPtNodeParams->getCodePointCount() - overlappingCodePointCount, - reallocatingPtNodeParams->getCodePoints() + overlappingCodePointCount, + reallocatingPtNodeParams->getCodePointArrayView().skip(overlappingCodePointCount), reallocatingPtNodeParams->getProbability())); if (!mPtNodeWriter->writePtNodeAndAdvancePosition(&childPartPtNodeParams, &writingPos)) { return false; @@ -258,8 +251,8 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( const PtNodeParams extraChildPtNodeParams(getPtNodeParamsForNewPtNode( unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), true /* isTerminal */, firstPartOfReallocatedPtNodePos, - newNodeCodePointCount - overlappingCodePointCount, - newNodeCodePoints + overlappingCodePointCount, unigramProperty->getProbability())); + newPtNodeCodePoints.skip(overlappingCodePointCount), + unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&extraChildPtNodeParams, unigramProperty, &writingPos)) { return false; @@ -282,26 +275,24 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( } const PtNodeParams DynamicPtUpdatingHelper::getUpdatedPtNodeParams( - const PtNodeParams *const originalPtNodeParams, - const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, const int parentPos, - const int codePointCount, const int *const codePoints, const int probability) const { + const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, + const bool isBlacklisted, const bool isTerminal, const int parentPos, + const CodePointArrayView codePoints, const int probability) const { const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( isBlacklisted, isNotAWord, isTerminal, false /* hasShortcutTargets */, - false /* hasBigrams */, codePointCount > 1 /* hasMultipleChars */, + false /* hasBigrams */, codePoints.size() > 1u /* hasMultipleChars */, CHILDREN_POSITION_FIELD_SIZE); - return PtNodeParams(originalPtNodeParams, flags, parentPos, codePointCount, codePoints, - probability); + return PtNodeParams(originalPtNodeParams, flags, parentPos, codePoints, probability); } -const PtNodeParams DynamicPtUpdatingHelper::getPtNodeParamsForNewPtNode( - const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, - const int parentPos, const int codePointCount, const int *const codePoints, - const int probability) const { +const PtNodeParams DynamicPtUpdatingHelper::getPtNodeParamsForNewPtNode(const bool isNotAWord, + const bool isBlacklisted, const bool isTerminal, const int parentPos, + const CodePointArrayView codePoints, const int probability) const { const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( isBlacklisted, isNotAWord, isTerminal, false /* hasShortcutTargets */, - false /* hasBigrams */, codePointCount > 1 /* hasMultipleChars */, + false /* hasBigrams */, codePoints.size() > 1u /* hasMultipleChars */, CHILDREN_POSITION_FIELD_SIZE); - return PtNodeParams(flags, parentPos, codePointCount, codePoints, probability); + return PtNodeParams(flags, parentPos, codePoints, probability); } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h index 97c05c1ea..710047e8c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h @@ -40,19 +40,21 @@ class DynamicPtUpdatingHelper { // Add a word to the dictionary. If the word already exists, update the probability. bool addUnigramWord(DynamicPtReadingHelper *const readingHelper, - const int *const wordCodePoints, const int codePointCount, - const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram); + const CodePointArrayView wordCodePoints, const UnigramProperty *const unigramProperty, + bool *const outAddedNewUnigram); + // TODO: Remove after stopping supporting v402. // Add an n-gram entry. bool addNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, const int wordPos, const BigramProperty *const bigramProperty, bool *const outAddedNewEntry); + // TODO: Remove after stopping supporting v402. // Remove an n-gram entry. bool removeNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, const int wordPos); // Add a shortcut target. - bool addShortcutTarget(const int wordPos, const int *const targetCodePoints, - const int targetCodePointCount, const int shortcutProbability); + bool addShortcutTarget(const int wordPos, const CodePointArrayView targetCodePoints, + const int shortcutProbability); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPtUpdatingHelper); @@ -63,33 +65,32 @@ class DynamicPtUpdatingHelper { const PtNodeReader *const mPtNodeReader; PtNodeWriter *const mPtNodeWriter; - bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints, - const int nodeCodePointCount, const UnigramProperty *const unigramProperty, + bool createAndInsertNodeIntoPtNodeArray(const int parentPos, + const CodePointArrayView ptNodeCodePoints, const UnigramProperty *const unigramProperty, int *const forwardLinkFieldPos); bool setPtNodeProbability(const PtNodeParams *const originalPtNodeParams, const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram); bool createChildrenPtNodeArrayAndAChildPtNode(const PtNodeParams *const parentPtNodeParams, - const UnigramProperty *const unigramProperty, const int *const codePoints, - const int codePointCount); + const UnigramProperty *const unigramProperty, + const CodePointArrayView remainingCodePoints); - bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, const int *const nodeCodePoints, - const int nodeCodePointCount, const UnigramProperty *const unigramProperty); + bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, + const CodePointArrayView ptNodeCodePoints, + const UnigramProperty *const unigramProperty); - bool reallocatePtNodeAndAddNewPtNodes( - const PtNodeParams *const reallocatingPtNodeParams, const int overlappingCodePointCount, - const UnigramProperty *const unigramProperty, const int *const newNodeCodePoints, - const int newNodeCodePointCount); + bool reallocatePtNodeAndAddNewPtNodes(const PtNodeParams *const reallocatingPtNodeParams, + const size_t overlappingCodePointCount, const UnigramProperty *const unigramProperty, + const CodePointArrayView newPtNodeCodePoints); const PtNodeParams getUpdatedPtNodeParams(const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, - const int parentPos, const int codePointCount, - const int *const codePoints, const int probability) const; + const int parentPos, const CodePointArrayView codePoints, const int probability) const; const PtNodeParams getPtNodeParamsForNewPtNode(const bool isNotAWord, const bool isBlacklisted, - const bool isTerminal, const int parentPos, - const int codePointCount, const int *const codePoints, const int probability) const; + const bool isTerminal, const int parentPos, const CodePointArrayView codePoints, + const int probability) const; }; } // namespace latinime #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_UPDATING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h index c12fed324..3ff1829bd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h @@ -89,9 +89,9 @@ class PtNodeParams { // Construct new params by updating existing PtNode params. PtNodeParams(const PtNodeParams *const ptNodeParams, const PatriciaTrieReadingUtils::NodeFlags flags, const int parentPos, - const int codePointCount, const int *const codePoints, const int probability) + const CodePointArrayView codePoints, const int probability) : mHeadPos(ptNodeParams->getHeadPos()), mFlags(flags), mHasMovedFlag(true), - mParentPos(parentPos), mCodePointCount(codePointCount), mCodePoints(), + mParentPos(parentPos), mCodePointCount(codePoints.size()), mCodePoints(), mTerminalIdFieldPos(ptNodeParams->getTerminalIdFieldPos()), mTerminalId(ptNodeParams->getTerminalId()), mProbabilityFieldPos(ptNodeParams->getProbabilityFieldPos()), @@ -102,20 +102,20 @@ class PtNodeParams { mShortcutPos(ptNodeParams->getShortcutPos()), mBigramPos(ptNodeParams->getBigramsPos()), mSiblingPos(ptNodeParams->getSiblingNodePos()) { - memcpy(mCodePoints, codePoints, sizeof(int) * mCodePointCount); + memcpy(mCodePoints, codePoints.data(), sizeof(int) * mCodePointCount); } PtNodeParams(const PatriciaTrieReadingUtils::NodeFlags flags, const int parentPos, - const int codePointCount, const int *const codePoints, const int probability) + const CodePointArrayView codePoints, const int probability) : mHeadPos(NOT_A_DICT_POS), mFlags(flags), mHasMovedFlag(true), mParentPos(parentPos), - mCodePointCount(codePointCount), mCodePoints(), + mCodePointCount(codePoints.size()), mCodePoints(), mTerminalIdFieldPos(NOT_A_DICT_POS), mTerminalId(Ver4DictConstants::NOT_A_TERMINAL_ID), mProbabilityFieldPos(NOT_A_DICT_POS), mProbability(probability), mChildrenPosFieldPos(NOT_A_DICT_POS), mChildrenPos(NOT_A_DICT_POS), mBigramLinkedNodePos(NOT_A_DICT_POS), mShortcutPos(NOT_A_DICT_POS), mBigramPos(NOT_A_DICT_POS), mSiblingPos(NOT_A_DICT_POS) { - memcpy(mCodePoints, codePoints, sizeof(int) * mCodePointCount); + memcpy(mCodePoints, codePoints.data(), sizeof(int) * mCodePointCount); } AK_FORCE_INLINE bool isValid() const { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp index 91c76941c..7cb7dff9a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp @@ -31,21 +31,21 @@ const int ShortcutListReadingUtils::SHORTCUT_LIST_SIZE_FIELD_SIZE = 2; const int ShortcutListReadingUtils::WHITELIST_SHORTCUT_PROBABILITY = 15; /* static */ ShortcutListReadingUtils::ShortcutFlags - ShortcutListReadingUtils::getFlagsAndForwardPointer(const uint8_t *const dictRoot, + ShortcutListReadingUtils::getFlagsAndForwardPointer(const ReadOnlyByteArrayView buffer, int *const pos) { - return ByteArrayUtils::readUint8AndAdvancePosition(dictRoot, pos); + return ByteArrayUtils::readUint8AndAdvancePosition(buffer.data(), pos); } /* static */ int ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer( - const uint8_t *const dictRoot, int *const pos) { + const ReadOnlyByteArrayView buffer, int *const pos) { // readUint16andAdvancePosition() returns an offset *including* the uint16 field itself. - return ByteArrayUtils::readUint16AndAdvancePosition(dictRoot, pos) + return ByteArrayUtils::readUint16AndAdvancePosition(buffer.data(), pos) - SHORTCUT_LIST_SIZE_FIELD_SIZE; } -/* static */ int ShortcutListReadingUtils::readShortcutTarget( - const uint8_t *const dictRoot, const int maxLength, int *const outWord, int *const pos) { - return ByteArrayUtils::readStringAndAdvancePosition(dictRoot, maxLength, outWord, pos); +/* static */ int ShortcutListReadingUtils::readShortcutTarget(const ReadOnlyByteArrayView buffer, + const int maxLength, int *const outWord, int *const pos) { + return ByteArrayUtils::readStringAndAdvancePosition(buffer.data(), maxLength, outWord, pos); } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h index d065bf7fd..71cb8cc2c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h @@ -20,6 +20,7 @@ #include <cstdint> #include "defines.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -27,7 +28,8 @@ class ShortcutListReadingUtils { public: typedef uint8_t ShortcutFlags; - static ShortcutFlags getFlagsAndForwardPointer(const uint8_t *const dictRoot, int *const pos); + static ShortcutFlags getFlagsAndForwardPointer(const ReadOnlyByteArrayView buffer, + int *const pos); static AK_FORCE_INLINE int getProbabilityFromFlags(const ShortcutFlags flags) { return flags & MASK_ATTRIBUTE_PROBABILITY; @@ -39,14 +41,15 @@ class ShortcutListReadingUtils { // This method returns the size of the shortcut list region excluding the shortcut list size // field at the beginning. - static int getShortcutListSizeAndForwardPointer(const uint8_t *const dictRoot, int *const pos); + static int getShortcutListSizeAndForwardPointer(const ReadOnlyByteArrayView buffer, + int *const pos); static AK_FORCE_INLINE int getShortcutListSizeFieldSize() { return SHORTCUT_LIST_SIZE_FIELD_SIZE; } - static AK_FORCE_INLINE void skipShortcuts(const uint8_t *const dictRoot, int *const pos) { - const int shortcutListSize = getShortcutListSizeAndForwardPointer(dictRoot, pos); + static AK_FORCE_INLINE void skipShortcuts(const ReadOnlyByteArrayView buffer, int *const pos) { + const int shortcutListSize = getShortcutListSizeAndForwardPointer(buffer, pos); *pos += shortcutListSize; } @@ -54,7 +57,7 @@ class ShortcutListReadingUtils { return getProbabilityFromFlags(flags) == WHITELIST_SHORTCUT_PROBABILITY; } - static int readShortcutTarget(const uint8_t *const dictRoot, const int maxLength, + static int readShortcutTarget(const ReadOnlyByteArrayView buffer, const int maxLength, int *const outWord, int *const pos); private: diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h index 73e291ec2..e2608435c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h @@ -22,22 +22,22 @@ #include "defines.h" #include "suggest/core/policy/dictionary_bigrams_structure_policy.h" #include "suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h" +#include "utils/byte_array_view.h" namespace latinime { class BigramListPolicy : public DictionaryBigramsStructurePolicy { public: - BigramListPolicy(const uint8_t *const bigramsBuf, const int bufSize) - : mBigramsBuf(bigramsBuf), mBufSize(bufSize) {} + BigramListPolicy(const ReadOnlyByteArrayView buffer) : mBuffer(buffer) {} ~BigramListPolicy() {} void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, int *const pos) const { BigramListReadWriteUtils::BigramFlags flags; - if (!BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(mBigramsBuf, - mBufSize, &flags, outBigramPos, pos)) { - AKLOGE("Cannot read bigram entry. mBufSize: %d, pos: %d. ", mBufSize, *pos); + if (!BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(mBuffer, &flags, + outBigramPos, pos)) { + AKLOGE("Cannot read bigram entry. bufSize: %zd, pos: %d. ", mBuffer.size(), *pos); *outProbability = NOT_A_PROBABILITY; *outHasNext = false; return; @@ -47,14 +47,13 @@ class BigramListPolicy : public DictionaryBigramsStructurePolicy { } bool skipAllBigrams(int *const pos) const { - return BigramListReadWriteUtils::skipExistingBigrams(mBigramsBuf, mBufSize, pos); + return BigramListReadWriteUtils::skipExistingBigrams(mBuffer, pos); } private: DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListPolicy); - const uint8_t *const mBigramsBuf; - const int mBufSize; + const ReadOnlyByteArrayView mBuffer; }; } // namespace latinime #endif // LATINIME_BIGRAM_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp index aae61afca..64b767dac 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp @@ -37,19 +37,19 @@ void PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNo return; } int nextPos = dicNode->getChildrenPtNodeArrayPos(); - if (nextPos < 0 || nextPos >= mDictBufferSize) { - AKLOGE("Children PtNode array position is invalid. pos: %d, dict size: %d", - nextPos, mDictBufferSize); + if (!isValidPos(nextPos)) { + AKLOGE("Children PtNode array position is invalid. pos: %d, dict size: %zd", + nextPos, mBuffer.size()); mIsCorrupted = true; ASSERT(false); return; } const int childCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition( - mDictRoot, &nextPos); + mBuffer.data(), &nextPos); for (int i = 0; i < childCount; i++) { - if (nextPos < 0 || nextPos >= mDictBufferSize) { - AKLOGE("Child PtNode position is invalid. pos: %d, dict size: %d, childCount: %d / %d", - nextPos, mDictBufferSize, i, childCount); + if (!isValidPos(nextPos)) { + AKLOGE("Child PtNode position is invalid. pos: %d, dict size: %zd, childCount: %d / %d", + nextPos, mBuffer.size(), i, childCount); mIsCorrupted = true; ASSERT(false); return; @@ -91,56 +91,57 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( int lastCandidatePtNodePos = 0; // Let's loop through PtNodes in this PtNode array searching for either the terminal // or one of its ascendants. - if (pos < 0 || pos >= mDictBufferSize) { - AKLOGE("PtNode array position is invalid. pos: %d, dict size: %d", - pos, mDictBufferSize); + if (!isValidPos(pos)) { + AKLOGE("PtNode array position is invalid. pos: %d, dict size: %zd", + pos, mBuffer.size()); mIsCorrupted = true; ASSERT(false); *outUnigramProbability = NOT_A_PROBABILITY; return 0; } for (int ptNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition( - mDictRoot, &pos); ptNodeCount > 0; --ptNodeCount) { + mBuffer.data(), &pos); ptNodeCount > 0; --ptNodeCount) { const int startPos = pos; - if (pos < 0 || pos >= mDictBufferSize) { - AKLOGE("PtNode position is invalid. pos: %d, dict size: %d", pos, mDictBufferSize); + if (!isValidPos(pos)) { + AKLOGE("PtNode position is invalid. pos: %d, dict size: %zd", pos, mBuffer.size()); mIsCorrupted = true; ASSERT(false); *outUnigramProbability = NOT_A_PROBABILITY; return 0; } const PatriciaTrieReadingUtils::NodeFlags flags = - PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos); + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mBuffer.data(), &pos); const int character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( - mDictRoot, &pos); + mBuffer.data(), &pos); if (ptNodePos == startPos) { // We found the position. Copy the rest of the code points in the buffer and return // the length. outCodePoints[wordPos] = character; if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) { int nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( - mDictRoot, &pos); + mBuffer.data(), &pos); // We count code points in order to avoid infinite loops if the file is broken // or if there is some other bug int charCount = maxCodePointCount; while (NOT_A_CODE_POINT != nextChar && --charCount > 0) { outCodePoints[++wordPos] = nextChar; nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( - mDictRoot, &pos); + mBuffer.data(), &pos); } } *outUnigramProbability = - PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, + PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mBuffer.data(), &pos); return ++wordPos; } // We need to skip past this PtNode, so skip any remaining code points after the // first and possibly the probability. if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) { - PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos); + PatriciaTrieReadingUtils::skipCharacters(mBuffer.data(), flags, MAX_WORD_LENGTH, + &pos); } if (PatriciaTrieReadingUtils::isTerminal(flags)) { - PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos); + PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mBuffer.data(), &pos); } // The fact that this PtNode has children is very important. Since we already know // that this PtNode does not match, if it has no children we know it is irrelevant @@ -155,7 +156,8 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( int currentPos = pos; // Here comes the tricky part. First, read the children position. const int childrenPos = PatriciaTrieReadingUtils - ::readChildrenPositionAndAdvancePosition(mDictRoot, flags, ¤tPos); + ::readChildrenPositionAndAdvancePosition(mBuffer.data(), flags, + ¤tPos); if (childrenPos > ptNodePos) { // If the children pos is greater than the position, it means the previous // PtNode, which position is stored in lastCandidatePtNodePos, was the right @@ -185,30 +187,30 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( if (0 != lastCandidatePtNodePos) { const PatriciaTrieReadingUtils::NodeFlags lastFlags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition( - mDictRoot, &lastCandidatePtNodePos); + mBuffer.data(), &lastCandidatePtNodePos); const int lastChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( - mDictRoot, &lastCandidatePtNodePos); + mBuffer.data(), &lastCandidatePtNodePos); // We copy all the characters in this PtNode to the buffer outCodePoints[wordPos] = lastChar; if (PatriciaTrieReadingUtils::hasMultipleChars(lastFlags)) { int nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( - mDictRoot, &lastCandidatePtNodePos); + mBuffer.data(), &lastCandidatePtNodePos); int charCount = maxCodePointCount; while (-1 != nextChar && --charCount > 0) { outCodePoints[++wordPos] = nextChar; nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( - mDictRoot, &lastCandidatePtNodePos); + mBuffer.data(), &lastCandidatePtNodePos); } } ++wordPos; // Now we only need to branch to the children address. Skip the probability if // it's there, read pos, and break to resume the search at pos. if (PatriciaTrieReadingUtils::isTerminal(lastFlags)) { - PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, + PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mBuffer.data(), &lastCandidatePtNodePos); } pos = PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition( - mDictRoot, lastFlags, &lastCandidatePtNodePos); + mBuffer.data(), lastFlags, &lastCandidatePtNodePos); break; } else { // Here is a little tricky part: we come here if we found out that all children @@ -220,14 +222,14 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( // ready to start the next one. if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) { PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition( - mDictRoot, flags, &pos); + mBuffer.data(), flags, &pos); } if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) { mShortcutListPolicy.skipAllShortcuts(&pos); } if (PatriciaTrieReadingUtils::hasBigrams(flags)) { if (!mBigramListPolicy.skipAllBigrams(&pos)) { - AKLOGE("Cannot skip bigrams. BufSize: %d, pos: %d.", mDictBufferSize, + AKLOGE("Cannot skip bigrams. BufSize: %zd, pos: %d.", mBuffer.size(), pos); mIsCorrupted = true; ASSERT(false); @@ -244,14 +246,14 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( // our pos is after the end of this PtNode, at the start of the next one. if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) { PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition( - mDictRoot, flags, &pos); + mBuffer.data(), flags, &pos); } if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) { mShortcutListPolicy.skipAllShortcuts(&pos); } if (PatriciaTrieReadingUtils::hasBigrams(flags)) { if (!mBigramListPolicy.skipAllBigrams(&pos)) { - AKLOGE("Cannot skip bigrams. BufSize: %d, pos: %d.", mDictBufferSize, pos); + AKLOGE("Cannot skip bigrams. BufSize: %zd, pos: %d.", mBuffer.size(), pos); mIsCorrupted = true; ASSERT(false); *outUnigramProbability = NOT_A_PROBABILITY; @@ -402,7 +404,7 @@ int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNod int shortcutPos = NOT_A_DICT_POS; int bigramPos = NOT_A_DICT_POS; int siblingPos = NOT_A_DICT_POS; - PatriciaTrieReadingUtils::readPtNodeInfo(mDictRoot, ptNodePos, &mShortcutListPolicy, + PatriciaTrieReadingUtils::readPtNodeInfo(mBuffer.data(), ptNodePos, &mShortcutListPolicy, &mBigramListPolicy, &flags, &mergedNodeCodePointCount, mergedNodeCodePoints, &probability, &childrenPos, &shortcutPos, &bigramPos, &siblingPos); // Skip PtNodes don't start with Unicode code point because they represent non-word information. @@ -452,14 +454,14 @@ const WordProperty PatriciaTriePolicy::getWordProperty( int shortcutPos = getShortcutPositionOfPtNode(ptNodePos); if (shortcutPos != NOT_A_DICT_POS) { int shortcutTargetCodePoints[MAX_WORD_LENGTH]; - ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(mDictRoot, &shortcutPos); + ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(mBuffer, &shortcutPos); bool hasNext = true; while (hasNext) { const ShortcutListReadingUtils::ShortcutFlags shortcutFlags = - ShortcutListReadingUtils::getFlagsAndForwardPointer(mDictRoot, &shortcutPos); + ShortcutListReadingUtils::getFlagsAndForwardPointer(mBuffer, &shortcutPos); hasNext = ShortcutListReadingUtils::hasNext(shortcutFlags); const int shortcutTargetLength = ShortcutListReadingUtils::readShortcutTarget( - mDictRoot, MAX_WORD_LENGTH, shortcutTargetCodePoints, &shortcutPos); + mBuffer, MAX_WORD_LENGTH, shortcutTargetCodePoints, &shortcutPos); const std::vector<int> shortcutTarget(shortcutTargetCodePoints, shortcutTargetCodePoints + shortcutTargetLength); const int shortcutProbability = @@ -512,4 +514,9 @@ int PatriciaTriePolicy::getWordIdFromTerminalPtNodePos(const int ptNodePos) cons int PatriciaTriePolicy::getTerminalPtNodePosFromWordId(const int wordId) const { return wordId == NOT_A_WORD_ID ? NOT_A_DICT_POS : wordId; } + +bool PatriciaTriePolicy::isValidPos(const int pos) const { + return pos >= 0 && pos < static_cast<int>(mBuffer.size()); +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h index fc65de58c..70e8d847e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h @@ -44,14 +44,11 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { : mMmappedBuffer(std::move(mmappedBuffer)), mHeaderPolicy(mMmappedBuffer->getReadOnlyByteArrayView().data(), FormatUtils::VERSION_2), - mDictRoot(mMmappedBuffer->getReadOnlyByteArrayView().data() - + mHeaderPolicy.getSize()), - mDictBufferSize(mMmappedBuffer->getReadOnlyByteArrayView().size() - - mHeaderPolicy.getSize()), - mBigramListPolicy(mDictRoot, mDictBufferSize), mShortcutListPolicy(mDictRoot), - mPtNodeReader(mDictRoot, mDictBufferSize, &mBigramListPolicy, &mShortcutListPolicy), - mPtNodeArrayReader(mDictRoot, mDictBufferSize), - mTerminalPtNodePositionsForIteratingWords(), mIsCorrupted(false) {} + mBuffer(mMmappedBuffer->getReadOnlyByteArrayView().skip(mHeaderPolicy.getSize())), + mBigramListPolicy(mBuffer), mShortcutListPolicy(mBuffer), + mPtNodeReader(mBuffer, &mBigramListPolicy, &mShortcutListPolicy), + mPtNodeArrayReader(mBuffer), mTerminalPtNodePositionsForIteratingWords(), + mIsCorrupted(false) {} AK_FORCE_INLINE int getRootPosition() const { return 0; @@ -149,8 +146,7 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { const MmappedBuffer::MmappedBufferPtr mMmappedBuffer; const HeaderPolicy mHeaderPolicy; - const uint8_t *const mDictRoot; - const int mDictBufferSize; + const ReadOnlyByteArrayView mBuffer; const BigramListPolicy mBigramListPolicy; const ShortcutListPolicy mShortcutListPolicy; const Ver2ParticiaTrieNodeReader mPtNodeReader; @@ -166,6 +162,7 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getTerminalPtNodePosFromWordId(const int wordId) const; const WordAttributes getWordAttributes(const int probability, const PtNodeParams &ptNodeParams) const; + bool isValidPos(const int pos) const; }; } // namespace latinime #endif // LATINIME_PATRICIA_TRIE_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h index 8e16ccc05..5319dd26c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h @@ -22,13 +22,13 @@ #include "defines.h" #include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" #include "suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h" +#include "utils/byte_array_view.h" namespace latinime { class ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { public: - explicit ShortcutListPolicy(const uint8_t *const shortcutBuf) - : mShortcutsBuf(shortcutBuf) {} + explicit ShortcutListPolicy(const ReadOnlyByteArrayView buffer) : mBuffer(buffer) {} ~ShortcutListPolicy() {} @@ -37,7 +37,7 @@ class ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { return NOT_A_DICT_POS; } int listPos = pos; - ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(mShortcutsBuf, &listPos); + ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(mBuffer, &listPos); return listPos; } @@ -45,7 +45,7 @@ class ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext, int *const pos) const { const ShortcutListReadingUtils::ShortcutFlags flags = - ShortcutListReadingUtils::getFlagsAndForwardPointer(mShortcutsBuf, pos); + ShortcutListReadingUtils::getFlagsAndForwardPointer(mBuffer, pos); if (outHasNext) { *outHasNext = ShortcutListReadingUtils::hasNext(flags); } @@ -54,20 +54,20 @@ class ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { } if (outCodePoint) { *outCodePointCount = ShortcutListReadingUtils::readShortcutTarget( - mShortcutsBuf, maxCodePointCount, outCodePoint, pos); + mBuffer, maxCodePointCount, outCodePoint, pos); } } void skipAllShortcuts(int *const pos) const { const int shortcutListSize = ShortcutListReadingUtils - ::getShortcutListSizeAndForwardPointer(mShortcutsBuf, pos); + ::getShortcutListSizeAndForwardPointer(mBuffer, pos); *pos += shortcutListSize; } private: DISALLOW_IMPLICIT_CONSTRUCTORS(ShortcutListPolicy); - const uint8_t *const mShortcutsBuf; + const ReadOnlyByteArrayView mBuffer; }; } // namespace latinime #endif // LATINIME_SHORTCUT_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp index c1e938710..74cdf7929 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp @@ -22,10 +22,10 @@ namespace latinime { const PtNodeParams Ver2ParticiaTrieNodeReader::fetchPtNodeParamsInBufferFromPtNodePos( const int ptNodePos) const { - if (ptNodePos < 0 || ptNodePos >= mDictSize) { + if (ptNodePos < 0 || ptNodePos >= static_cast<int>(mBuffer.size())) { // Reading invalid position because of bug or broken dictionary. - AKLOGE("Fetching PtNode info from invalid dictionary position: %d, dictionary size: %d", - ptNodePos, mDictSize); + AKLOGE("Fetching PtNode info from invalid dictionary position: %d, dictionary size: %zd", + ptNodePos, mBuffer.size()); ASSERT(false); return PtNodeParams(); } @@ -37,7 +37,7 @@ const PtNodeParams Ver2ParticiaTrieNodeReader::fetchPtNodeParamsInBufferFromPtNo int shortcutPos = NOT_A_DICT_POS; int bigramPos = NOT_A_DICT_POS; int siblingPos = NOT_A_DICT_POS; - PatriciaTrieReadingUtils::readPtNodeInfo(mDictBuffer, ptNodePos, mShortuctPolicy, + PatriciaTrieReadingUtils::readPtNodeInfo(mBuffer.data(), ptNodePos, mShortuctPolicy, mBigramPolicy, &flags, &mergedNodeCodePointCount, mergedNodeCodePoints, &probability, &childrenPos, &shortcutPos, &bigramPos, &siblingPos); if (mergedNodeCodePointCount <= 0) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h index f0725b66d..0f6769dc8 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h @@ -22,6 +22,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -30,19 +31,17 @@ class DictionaryShortcutsStructurePolicy; class Ver2ParticiaTrieNodeReader : public PtNodeReader { public: - Ver2ParticiaTrieNodeReader(const uint8_t *const dictBuffer, const int dictSize, + Ver2ParticiaTrieNodeReader(const ReadOnlyByteArrayView buffer, const DictionaryBigramsStructurePolicy *const bigramPolicy, const DictionaryShortcutsStructurePolicy *const shortcutPolicy) - : mDictBuffer(dictBuffer), mDictSize(dictSize), mBigramPolicy(bigramPolicy), - mShortuctPolicy(shortcutPolicy) {} + : mBuffer(buffer), mBigramPolicy(bigramPolicy), mShortuctPolicy(shortcutPolicy) {} virtual const PtNodeParams fetchPtNodeParamsInBufferFromPtNodePos(const int ptNodePos) const; private: DISALLOW_IMPLICIT_CONSTRUCTORS(Ver2ParticiaTrieNodeReader); - const uint8_t *const mDictBuffer; - const int mDictSize; + const ReadOnlyByteArrayView mBuffer; const DictionaryBigramsStructurePolicy *const mBigramPolicy; const DictionaryShortcutsStructurePolicy *const mShortuctPolicy; }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.cpp index b46617d96..72ad1eb66 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.cpp @@ -22,16 +22,16 @@ namespace latinime { bool Ver2PtNodeArrayReader::readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos, int *const outPtNodeCount, int *const outFirstPtNodePos) const { - if (ptNodeArrayPos < 0 || ptNodeArrayPos >= mDictSize) { + if (ptNodeArrayPos < 0 || ptNodeArrayPos >= static_cast<int>(mBuffer.size())) { // Reading invalid position because of a bug or a broken dictionary. - AKLOGE("Reading PtNode array info from invalid dictionary position: %d, dict size: %d", - ptNodeArrayPos, mDictSize); + AKLOGE("Reading PtNode array info from invalid dictionary position: %d, dict size: %zd", + ptNodeArrayPos, mBuffer.size()); ASSERT(false); return false; } int readingPos = ptNodeArrayPos; const int ptNodeCountInArray = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition( - mDictBuffer, &readingPos); + mBuffer.data(), &readingPos); *outPtNodeCount = ptNodeCountInArray; *outFirstPtNodePos = readingPos; return true; @@ -39,10 +39,10 @@ bool Ver2PtNodeArrayReader::readPtNodeArrayInfoAndReturnIfValid(const int ptNode bool Ver2PtNodeArrayReader::readForwardLinkAndReturnIfValid(const int forwordLinkPos, int *const outNextPtNodeArrayPos) const { - if (forwordLinkPos < 0 || forwordLinkPos >= mDictSize) { + if (forwordLinkPos < 0 || forwordLinkPos >= static_cast<int>(mBuffer.size())) { // Reading invalid position because of bug or broken dictionary. - AKLOGE("Reading forward link from invalid dictionary position: %d, dict size: %d", - forwordLinkPos, mDictSize); + AKLOGE("Reading forward link from invalid dictionary position: %d, dict size: %zd", + forwordLinkPos, mBuffer.size()); ASSERT(false); return false; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.h index 548272148..548f36bf3 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.h @@ -21,13 +21,13 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h" +#include "utils/byte_array_view.h" namespace latinime { class Ver2PtNodeArrayReader : public PtNodeArrayReader { public: - Ver2PtNodeArrayReader(const uint8_t *const dictBuffer, const int dictSize) - : mDictBuffer(dictBuffer), mDictSize(dictSize) {}; + Ver2PtNodeArrayReader(const ReadOnlyByteArrayView buffer) : mBuffer(buffer) {}; virtual bool readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos, int *const outPtNodeCount, int *const outFirstPtNodePos) const; @@ -37,8 +37,7 @@ class Ver2PtNodeArrayReader : public PtNodeArrayReader { private: DISALLOW_COPY_AND_ASSIGN(Ver2PtNodeArrayReader); - const uint8_t *const mDictBuffer; - const int mDictSize; + const ReadOnlyByteArrayView mBuffer; }; } // namespace latinime #endif /* LATINIME_VER2_PT_NODE_ARRAY_READER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp index f54bb151a..35f0f768f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp @@ -39,7 +39,7 @@ bool LanguageModelDictContent::runGC( } int LanguageModelDictContent::getWordProbability(const WordIdArrayView prevWordIds, - const int wordId) const { + const int wordId, const HeaderPolicy *const headerPolicy) const { int bitmapEntryIndices[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1]; bitmapEntryIndices[0] = mTrieMap.getRootBitmapEntryIndex(); int maxLevel = 0; @@ -58,14 +58,15 @@ int LanguageModelDictContent::getWordProbability(const WordIdArrayView prevWordI if (!result.mIsValid) { continue; } - const int probability = - ProbabilityEntry::decode(result.mValue, mHasHistoricalInfo).getProbability(); + const ProbabilityEntry probabilityEntry = + ProbabilityEntry::decode(result.mValue, mHasHistoricalInfo); if (mHasHistoricalInfo) { - return std::min( - probability + ForgettingCurveUtils::getProbabilityBiasForNgram(i + 1 /* n */), - MAX_PROBABILITY); + const int probability = ForgettingCurveUtils::decodeProbability( + probabilityEntry.getHistoricalInfo(), headerPolicy) + + ForgettingCurveUtils::getProbabilityBiasForNgram(i + 1 /* n */); + return std::min(probability, MAX_PROBABILITY); } else { - return probability; + return probabilityEntry.getProbability(); } } // Cannot find the word. @@ -166,7 +167,15 @@ int LanguageModelDictContent::createAndGetBitmapEntryIndex(const WordIdArrayView if (lastBitmapEntryIndex == TrieMap::INVALID_INDEX) { return TrieMap::INVALID_INDEX; } - return mTrieMap.getNextLevelBitmapEntryIndex(prevWordIds[prevWordIds.size() - 1], + const int oldestPrevWordId = prevWordIds.lastOrDefault(NOT_A_WORD_ID); + const TrieMap::Result result = mTrieMap.get(oldestPrevWordId, lastBitmapEntryIndex); + if (!result.mIsValid) { + if (!mTrieMap.put(oldestPrevWordId, + ProbabilityEntry().encode(mHasHistoricalInfo), lastBitmapEntryIndex)) { + return TrieMap::INVALID_INDEX; + } + } + return mTrieMap.getNextLevelBitmapEntryIndex(prevWordIds.lastOrDefault(NOT_A_WORD_ID), lastBitmapEntryIndex); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h index 4e0b47036..a793af4be 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h @@ -128,7 +128,8 @@ class LanguageModelDictContent { const LanguageModelDictContent *const originalContent, int *const outNgramCount); - int getWordProbability(const WordIdArrayView prevWordIds, const int wordId) const; + int getWordProbability(const WordIdArrayView prevWordIds, const int wordId, + const HeaderPolicy *const headerPolicy) const; ProbabilityEntry getProbabilityEntry(const int wordId) const { return getNgramProbabilityEntry(WordIdArrayView(), wordId); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h index 3dfaba755..f1bf12cb2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h @@ -36,7 +36,8 @@ class ProbabilityEntry { // Dummy entry ProbabilityEntry() - : mFlags(0), mProbability(NOT_A_PROBABILITY), mHistoricalInfo() {} + : mFlags(Ver4DictConstants::FLAG_NOT_A_VALID_ENTRY), mProbability(NOT_A_PROBABILITY), + mHistoricalInfo() {} // Entry without historical information ProbabilityEntry(const int flags, const int probability) @@ -61,7 +62,7 @@ class ProbabilityEntry { bigramProperty->getCount()) {} bool isValid() const { - return (mProbability != NOT_A_PROBABILITY) || hasHistoricalInfo(); + return (mFlags & Ver4DictConstants::FLAG_NOT_A_VALID_ENTRY) == 0; } bool hasHistoricalInfo() const { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp index 9acf2d44f..39822b94a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp @@ -53,6 +53,7 @@ const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 1; const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1; const uint8_t Ver4DictConstants::FLAG_REPRESENTS_BEGINNING_OF_SENTENCE = 0x1; +const uint8_t Ver4DictConstants::FLAG_NOT_A_VALID_ENTRY = 0x2; const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64; const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE = 4; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h index 97035311e..dfcdd4d6f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h @@ -51,6 +51,7 @@ class Ver4DictConstants { static const int WORD_COUNT_FIELD_SIZE; // Flags in probability entry. static const uint8_t FLAG_REPRESENTS_BEGINNING_OF_SENTENCE; + static const uint8_t FLAG_NOT_A_VALID_ENTRY; static const int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE; static const int SHORTCUT_ADDRESS_TABLE_DATA_SIZE; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp index 9ca712470..75ec16912 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -211,19 +211,17 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { - // TODO: Support n-gram. LanguageModelDictContent *const languageModelDictContent = mBuffers->getMutableLanguageModelDictContent(); const ProbabilityEntry probabilityEntry = - languageModelDictContent->getNgramProbabilityEntry( - prevWordIds.limit(1 /* maxSize */), wordId); + languageModelDictContent->getNgramProbabilityEntry(prevWordIds, wordId); const ProbabilityEntry probabilityEntryOfBigramProperty(bigramProperty); const ProbabilityEntry updatedProbabilityEntry = createUpdatedEntryFrom( &probabilityEntry, &probabilityEntryOfBigramProperty); if (!languageModelDictContent->setNgramProbabilityEntry( - prevWordIds.limit(1 /* maxSize */), wordId, &updatedProbabilityEntry)) { - AKLOGE("Cannot add new ngram entry. prevWordId: %d, wordId: %d", - prevWordIds[0], wordId); + prevWordIds, wordId, &updatedProbabilityEntry)) { + AKLOGE("Cannot add new ngram entry. prevWordId[0]: %d, prevWordId.size(): %zd, wordId: %d", + prevWordIds[0], prevWordIds.size(), wordId); return false; } if (!probabilityEntry.isValid() && outAddedNewBigram) { @@ -234,11 +232,9 @@ bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds bool Ver4PatriciaTrieNodeWriter::removeNgramEntry(const WordIdArrayView prevWordIds, const int wordId) { - // TODO: Support n-gram. LanguageModelDictContent *const languageModelDictContent = mBuffers->getMutableLanguageModelDictContent(); - return languageModelDictContent->removeNgramProbabilityEntry(prevWordIds.limit(1 /* maxSize */), - wordId); + return languageModelDictContent->removeNgramProbabilityEntry(prevWordIds, wordId); } // TODO: Remove when we stop supporting v402 format. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp index 5b1907f49..8d4135679 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -120,15 +120,15 @@ const WordAttributes Ver4PatriciaTriePolicy::getWordAttributesInContext( const int ptNodePos = mBuffers->getTerminalPositionLookupTable()->getTerminalPtNodePosition(wordId); const PtNodeParams ptNodeParams = mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos); - // TODO: Support n-gram. - return WordAttributes(mBuffers->getLanguageModelDictContent()->getWordProbability( - prevWordIds.limit(1 /* maxSize */), wordId), ptNodeParams.isBlacklisted(), - ptNodeParams.isNotAWord(), ptNodeParams.getProbability() == 0); + const int probability = mBuffers->getLanguageModelDictContent()->getWordProbability( + prevWordIds, wordId, mHeaderPolicy); + return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(), + probability == 0); } int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordIds, const int wordId) const { - if (wordId == NOT_A_WORD_ID) { + if (wordId == NOT_A_WORD_ID || prevWordIds.contains(NOT_A_WORD_ID)) { return NOT_A_PROBABILITY; } const int ptNodePos = @@ -137,10 +137,8 @@ int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordI if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) { return NOT_A_PROBABILITY; } - // TODO: Support n-gram. const ProbabilityEntry probabilityEntry = - mBuffers->getLanguageModelDictContent()->getNgramProbabilityEntry( - prevWordIds.limit(1 /* maxSize */), wordId); + mBuffers->getLanguageModelDictContent()->getNgramProbabilityEntry(prevWordIds, wordId); if (!probabilityEntry.isValid()) { return NOT_A_PROBABILITY; } @@ -163,16 +161,18 @@ void Ver4PatriciaTriePolicy::iterateNgramEntries(const WordIdArrayView prevWordI if (prevWordIds.empty()) { return; } - // TODO: Support n-gram. const auto languageModelDictContent = mBuffers->getLanguageModelDictContent(); - for (const auto entry : languageModelDictContent->getProbabilityEntries( - prevWordIds.limit(1 /* maxSize */))) { - const ProbabilityEntry &probabilityEntry = entry.getProbabilityEntry(); - const int probability = probabilityEntry.hasHistoricalInfo() ? - ForgettingCurveUtils::decodeProbability( - probabilityEntry.getHistoricalInfo(), mHeaderPolicy) : - probabilityEntry.getProbability(); - listener->onVisitEntry(probability, entry.getWordId()); + for (size_t i = 1; i <= prevWordIds.size(); ++i) { + for (const auto entry : languageModelDictContent->getProbabilityEntries( + prevWordIds.limit(i))) { + const ProbabilityEntry &probabilityEntry = entry.getProbabilityEntry(); + const int probability = probabilityEntry.hasHistoricalInfo() ? + ForgettingCurveUtils::decodeProbability( + probabilityEntry.getHistoricalInfo(), mHeaderPolicy) + + ForgettingCurveUtils::getProbabilityBiasForNgram(i + 1 /* n */) : + probabilityEntry.getProbability(); + listener->onVisitEntry(probability, entry.getWordId()); + } } } @@ -227,8 +227,8 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const CodePointArrayView wordCodePo return false; } const CodePointArrayView codePointArrayView(codePointsToAdd, codePointCountToAdd); - if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointArrayView.data(), - codePointArrayView.size(), unigramProperty, &addedNewUnigram)) { + if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointArrayView, unigramProperty, + &addedNewUnigram)) { if (addedNewUnigram && !unigramProperty->representsBeginningOfSentence()) { mUnigramCount++; } @@ -243,8 +243,8 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const CodePointArrayView wordCodePo mBuffers->getTerminalPositionLookupTable()->getTerminalPtNodePosition(wordId); for (const auto &shortcut : unigramProperty->getShortcuts()) { if (!mUpdatingHelper.addShortcutTarget(wordPos, - shortcut.getTargetCodePoints()->data(), - shortcut.getTargetCodePoints()->size(), shortcut.getProbability())) { + CodePointArrayView(*shortcut.getTargetCodePoints()), + shortcut.getProbability())) { AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %zd, " "probability: %d", wordPos, shortcut.getTargetCodePoints()->size(), shortcut.getProbability()); @@ -303,26 +303,31 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI "length: %zd", bigramProperty->getTargetCodePoints()->size()); return false; } - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIds; - prevWordsInfo->getPrevWordIds(this, prevWordIds.data(), false /* tryLowerCaseSearch */); - // TODO: Support N-gram. - if (prevWordIds[0] == NOT_A_WORD_ID) { - if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) { - const std::vector<UnigramProperty::ShortcutProperty> shortcuts; - const UnigramProperty beginningOfSentenceUnigramProperty( - true /* representsBeginningOfSentence */, true /* isNotAWord */, - false /* isBlacklisted */, MAX_PROBABILITY /* probability */, - NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts); - if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */), - &beginningOfSentenceUnigramProperty)) { - AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); - return false; - } - // Refresh word ids. - prevWordsInfo->getPrevWordIds(this, prevWordIds.data(), false /* tryLowerCaseSearch */); - } else { + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + false /* tryLowerCaseSearch */); + if (prevWordIds.empty()) { + return false; + } + for (size_t i = 0; i < prevWordIds.size(); ++i) { + if (prevWordIds[i] != NOT_A_WORD_ID) { + continue; + } + if (!prevWordsInfo->isNthPrevWordBeginningOfSentence(i + 1 /* n */)) { + return false; + } + const std::vector<UnigramProperty::ShortcutProperty> shortcuts; + const UnigramProperty beginningOfSentenceUnigramProperty( + true /* representsBeginningOfSentence */, true /* isNotAWord */, + false /* isBlacklisted */, MAX_PROBABILITY /* probability */, + NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts); + if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */), + &beginningOfSentenceUnigramProperty)) { + AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); return false; } + // Refresh word ids. + prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, false /* tryLowerCaseSearch */); } const int wordId = getWordId(CodePointArrayView(*bigramProperty->getTargetCodePoints()), false /* forceLowerCaseSearch */); @@ -330,15 +335,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI return false; } bool addedNewEntry = false; - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordsPtNodePos; - for (size_t i = 0; i < prevWordsPtNodePos.size(); ++i) { - prevWordsPtNodePos[i] = mBuffers->getTerminalPositionLookupTable() - ->getTerminalPtNodePosition(prevWordIds[i]); - } - const int wordPtNodePos = mBuffers->getTerminalPositionLookupTable() - ->getTerminalPtNodePosition(wordId); - if (mUpdatingHelper.addNgramEntry(WordIdArrayView::fromArray(prevWordsPtNodePos), - wordPtNodePos, bigramProperty, &addedNewEntry)) { + if (mNodeWriter.addNgramEntry(prevWordIds, wordId, bigramProperty, &addedNewEntry)) { if (addedNewEntry) { mBigramCount++; } @@ -367,25 +364,17 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor AKLOGE("word is too long to remove n-gram entry form the dictionary. length: %zd", wordCodePoints.size()); } - WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIds; - prevWordsInfo->getPrevWordIds(this, prevWordIds.data(), false /* tryLowerCaseSerch */); - // TODO: Support N-gram. - if (prevWordIds[0] == NOT_A_WORD_ID) { + WordIdArray<MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordIdArray; + const WordIdArrayView prevWordIds = prevWordsInfo->getPrevWordIds(this, &prevWordIdArray, + false /* tryLowerCaseSerch */); + if (prevWordIds.empty() || prevWordIds.contains(NOT_A_WORD_ID)) { return false; } const int wordId = getWordId(wordCodePoints, false /* forceLowerCaseSearch */); if (wordId == NOT_A_WORD_ID) { return false; } - std::array<int, MAX_PREV_WORD_COUNT_FOR_N_GRAM> prevWordsPtNodePos; - for (size_t i = 0; i < prevWordsPtNodePos.size(); ++i) { - prevWordsPtNodePos[i] = mBuffers->getTerminalPositionLookupTable() - ->getTerminalPtNodePosition(prevWordIds[i]); - } - const int wordPtNodePos = mBuffers->getTerminalPositionLookupTable() - ->getTerminalPtNodePosition(wordId); - if (mUpdatingHelper.removeNgramEntry(WordIdArrayView::fromArray(prevWordsPtNodePos), - wordPtNodePos)) { + if (mNodeWriter.removeNgramEntry(prevWordIds, wordId)) { mBigramCount--; return true; } else { diff --git a/native/jni/src/utils/byte_array_view.h b/native/jni/src/utils/byte_array_view.h index 10d7ae278..2b778af6f 100644 --- a/native/jni/src/utils/byte_array_view.h +++ b/native/jni/src/utils/byte_array_view.h @@ -42,6 +42,13 @@ class ReadOnlyByteArrayView { return mPtr; } + AK_FORCE_INLINE const ReadOnlyByteArrayView skip(const size_t n) const { + if (mSize <= n) { + return ReadOnlyByteArrayView(); + } + return ReadOnlyByteArrayView(mPtr + n, mSize - n); + } + private: DISALLOW_ASSIGNMENT_OPERATOR(ReadOnlyByteArrayView); diff --git a/native/jni/src/utils/int_array_view.h b/native/jni/src/utils/int_array_view.h index caa13d976..f3a8589ca 100644 --- a/native/jni/src/utils/int_array_view.h +++ b/native/jni/src/utils/int_array_view.h @@ -17,6 +17,7 @@ #ifndef LATINIME_INT_ARRAY_VIEW_H #define LATINIME_INT_ARRAY_VIEW_H +#include <algorithm> #include <array> #include <cstdint> #include <cstring> @@ -92,12 +93,16 @@ class IntArrayView { return mPtr + mSize; } + AK_FORCE_INLINE bool contains(const int value) const { + return std::find(begin(), end(), value) != end(); + } + // Returns the view whose size is smaller than or equal to the given count. - const IntArrayView limit(const size_t maxSize) const { + AK_FORCE_INLINE const IntArrayView limit(const size_t maxSize) const { return IntArrayView(mPtr, std::min(maxSize, mSize)); } - const IntArrayView skip(const size_t n) const { + AK_FORCE_INLINE const IntArrayView skip(const size_t n) const { if (mSize <= n) { return IntArrayView(); } @@ -110,6 +115,20 @@ class IntArrayView { memmove(buffer->data() + offset, mPtr, sizeof(int) * mSize); } + AK_FORCE_INLINE int firstOrDefault(const int defaultValue) const { + if (empty()) { + return defaultValue; + } + return mPtr[0]; + } + + AK_FORCE_INLINE int lastOrDefault(const int defaultValue) const { + if (empty()) { + return defaultValue; + } + return mPtr[mSize - 1]; + } + private: DISALLOW_ASSIGNMENT_OPERATOR(IntArrayView); diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp index 7608b45c2..06f82df52 100644 --- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp +++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp @@ -29,7 +29,7 @@ namespace { TEST(LanguageModelDictContentTest, TestUnigramProbability) { LanguageModelDictContent languageModelDictContent(false /* useHistoricalInfo */); - const int flag = 0xFF; + const int flag = 0xF0; const int probability = 10; const int wordId = 100; const ProbabilityEntry probabilityEntry(flag, probability); @@ -107,13 +107,15 @@ TEST(LanguageModelDictContentTest, TestGetWordProbability) { languageModelDictContent.setProbabilityEntry(prevWordIds[0], &probabilityEntry); languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(1), wordId, &bigramProbabilityEntry); - EXPECT_EQ(bigramProbability, languageModelDictContent.getWordProbability(prevWordIds, wordId)); + EXPECT_EQ(bigramProbability, languageModelDictContent.getWordProbability(prevWordIds, wordId, + nullptr /* headerPolicy */)); const ProbabilityEntry trigramProbabilityEntry(flag, trigramProbability); languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(1), prevWordIds[1], &probabilityEntry); languageModelDictContent.setNgramProbabilityEntry(prevWordIds.limit(2), wordId, &trigramProbabilityEntry); - EXPECT_EQ(trigramProbability, languageModelDictContent.getWordProbability(prevWordIds, wordId)); + EXPECT_EQ(trigramProbability, languageModelDictContent.getWordProbability(prevWordIds, wordId, + nullptr /* headerPolicy */)); } } // namespace diff --git a/native/jni/tests/utils/int_array_view_test.cpp b/native/jni/tests/utils/int_array_view_test.cpp index 3bc294cdd..487bd04b1 100644 --- a/native/jni/tests/utils/int_array_view_test.cpp +++ b/native/jni/tests/utils/int_array_view_test.cpp @@ -58,6 +58,19 @@ TEST(IntArrayViewTest, TestConstructFromObject) { EXPECT_EQ(object, intArrayView[0]); } +TEST(IntArrayViewTest, TestContains) { + EXPECT_FALSE(IntArrayView().contains(0)); + EXPECT_FALSE(IntArrayView().contains(1)); + + const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; + IntArrayView intArrayView(intVector); + EXPECT_TRUE(intArrayView.contains(0)); + EXPECT_TRUE(intArrayView.contains(3)); + EXPECT_TRUE(intArrayView.contains(-2)); + EXPECT_FALSE(intArrayView.contains(-3)); + EXPECT_FALSE(intArrayView.limit(0).contains(3)); +} + TEST(IntArrayViewTest, TestLimit) { const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; IntArrayView intArrayView(intVector); @@ -111,5 +124,25 @@ TEST(IntArrayViewTest, TestCopyToArray) { EXPECT_EQ(70, buffer[6]); } +TEST(IntArrayViewTest, TestFirstOrDefault) { + const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; + IntArrayView intArrayView(intVector); + + EXPECT_EQ(3, intArrayView.firstOrDefault(10)); + EXPECT_EQ(10, intArrayView.limit(0).firstOrDefault(10)); + EXPECT_EQ(-10, intArrayView.limit(0).firstOrDefault(-10)); + EXPECT_EQ(10, intArrayView.skip(6).firstOrDefault(10)); +} + +TEST(IntArrayViewTest, TestLastOrDefault) { + const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; + IntArrayView intArrayView(intVector); + + EXPECT_EQ(-2, intArrayView.lastOrDefault(10)); + EXPECT_EQ(10, intArrayView.limit(0).lastOrDefault(10)); + EXPECT_EQ(-10, intArrayView.limit(0).lastOrDefault(-10)); + EXPECT_EQ(10, intArrayView.skip(6).lastOrDefault(10)); +} + } // namespace } // namespace latinime diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 4ca846be1..8a628cdac 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.inputmethod.latin.tests"> - <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java index 570865738..b64ab8c80 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java @@ -51,7 +51,7 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); final KeyboardTheme keyboardTheme = KeyboardTheme.searchKeyboardThemeById( - getKeyboardThemeForTests()); + getKeyboardThemeForTests(), KeyboardTheme.KEYBOARD_THEMES); setContext(new ContextThemeWrapper(getContext(), keyboardTheme.mStyleId)); KeyboardLayoutSet.onKeyboardThemeChanged(); diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java index c20954f81..34cf4072f 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java @@ -28,6 +28,8 @@ import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import java.util.Arrays; + @SmallTest public class KeyboardThemeTests extends AndroidTestCase { private SharedPreferences mPrefs; @@ -77,7 +79,9 @@ public class KeyboardThemeTests extends AndroidTestCase { } private void assertKeyboardTheme(final int sdkVersion, final int expectedThemeId) { - assertEquals(expectedThemeId, KeyboardTheme.getKeyboardTheme(mPrefs, sdkVersion).mThemeId); + final KeyboardTheme actualTheme = KeyboardTheme.getKeyboardTheme( + mPrefs, sdkVersion, KeyboardTheme.KEYBOARD_THEMES); + assertEquals(expectedThemeId, actualTheme.mThemeId); } /* @@ -139,8 +143,8 @@ public class KeyboardThemeTests extends AndroidTestCase { final String oldPrefKey = KeyboardTheme.KLP_KEYBOARD_THEME_KEY; setKeyboardThemePreference(oldPrefKey, previousThemeId); - final KeyboardTheme defaultTheme = - KeyboardTheme.getDefaultKeyboardTheme(mPrefs, sdkVersion); + final KeyboardTheme defaultTheme = KeyboardTheme.getDefaultKeyboardTheme( + mPrefs, sdkVersion, KeyboardTheme.KEYBOARD_THEMES); assertNotNull(defaultTheme); assertEquals(expectedThemeId, defaultTheme.mThemeId); @@ -194,7 +198,8 @@ public class KeyboardThemeTests extends AndroidTestCase { // Clean up new keyboard theme preference to simulate "upgrade to LXX keyboard". setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); - final KeyboardTheme theme = KeyboardTheme.getKeyboardTheme(mPrefs, sdkVersion); + final KeyboardTheme theme = KeyboardTheme.getKeyboardTheme( + mPrefs, sdkVersion, KeyboardTheme.KEYBOARD_THEMES); assertNotNull(theme); assertEquals(expectedThemeId, theme.mThemeId); @@ -341,4 +346,60 @@ public class KeyboardThemeTests extends AndroidTestCase { assertUpgradePlatformFromTo( oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_LIGHT); } + + /* + * Test for missing selected theme. + */ + private static KeyboardTheme[] LIMITED_THEMES = { + KeyboardTheme.searchKeyboardThemeById(THEME_ID_ICS, KeyboardTheme.KEYBOARD_THEMES), + KeyboardTheme.searchKeyboardThemeById(THEME_ID_KLP, KeyboardTheme.KEYBOARD_THEMES) + }; + static { + Arrays.sort(LIMITED_THEMES); + } + + public void testMissingSelectedThemeIcs() { + // Clean up preferences. + setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL); + setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); + + final int sdkVersion = VERSION_CODES.ICE_CREAM_SANDWICH; + final String oldPrefKey = KeyboardTheme.getPreferenceKey(sdkVersion); + setKeyboardThemePreference(oldPrefKey, THEME_ID_LXX_LIGHT); + + final KeyboardTheme actualTheme = KeyboardTheme.getKeyboardTheme( + mPrefs, sdkVersion, LIMITED_THEMES); + // LXX_LIGHT is missing, fall-back to KLP. + assertEquals(THEME_ID_KLP, actualTheme.mThemeId); + } + + public void testMissingSelectedThemeKlp() { + // Clean up preferences. + setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL); + setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); + + final int sdkVersion = VERSION_CODES.KITKAT; + final String oldPrefKey = KeyboardTheme.getPreferenceKey(sdkVersion); + setKeyboardThemePreference(oldPrefKey, THEME_ID_LXX_LIGHT); + + final KeyboardTheme actualTheme = KeyboardTheme.getKeyboardTheme( + mPrefs, sdkVersion, LIMITED_THEMES); + // LXX_LIGHT is missing, fall-back to KLP. + assertEquals(THEME_ID_KLP, actualTheme.mThemeId); + } + + public void testMissingSelectedThemeLxx() { + // Clean up preferences. + setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL); + setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); + + final int sdkVersion = VERSION_CODES_LXX; + final String oldPrefKey = KeyboardTheme.getPreferenceKey(sdkVersion); + setKeyboardThemePreference(oldPrefKey, THEME_ID_LXX_DARK); + + final KeyboardTheme actualTheme = KeyboardTheme.getKeyboardTheme( + mPrefs, sdkVersion, LIMITED_THEMES); + // LXX_DARK is missing, fall-back to KLP. + assertEquals(THEME_ID_KLP, actualTheme.mThemeId); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index d7a649a5b..6860bea45 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -183,6 +183,9 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { | InputType.TYPE_TEXT_FLAG_MULTI_LINE; mEditText.setInputType(inputType); mEditText.setEnabled(true); + if (null == Looper.myLooper()) { + Looper.prepare(); + } setupService(); mLatinIME = getService(); setDebugMode(true); diff --git a/tests/src/com/android/inputmethod/latin/utils/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/utils/DistracterFilterTest.java index 5fbd36ac7..6ed912088 100644 --- a/tests/src/com/android/inputmethod/latin/utils/DistracterFilterTest.java +++ b/tests/src/com/android/inputmethod/latin/utils/DistracterFilterTest.java @@ -57,7 +57,7 @@ public class DistracterFilterTest extends AndroidTestCase { mDistracterFilter.close(); } - public void testIsDistractorToWordsInDictionaries() { + public void testIsDistracterToWordsInDictionaries() { final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; final Locale localeEnUs = new Locale("en", "US"); |